TypeScript 3.8 Beta 追加機能等まとめ

あけましておめでとうございます。

ちょっと説明不足なところがあるので、追って追加します。
# TypeScript 3.8　Beta

2020年1月10日にTypeScriptのver 3.8のBeta版が公開されました。少し前に3.7.2が公開されたような気がするので、けっこうはやいなあと思いました。Betaですからこんなものなのでしょうか。


いろいろ新しい機能が追加されたみたいなので簡単にまとめてみました。（自分がTypeScript初心者&&英語が苦手なので、間違いなどがあったら指摘してほしいです！）

## インストール

NuGetを使う方法と、npmを使ってインストールする方法がありますが、自分はVSCodeを使ってTypeScriptを書いているのでnpmを使いました。

```
npm install typescript@beta
```
# 追加機能
結構多いのでざっと概要だけ書きます。

## Type-Only Imports and Export

型のみをインポート、エクスポートすることができるようになります。
これを用いて、インポートする際などに型を明示的に扱うことができます。
構文は以下の通り

```typescript
import type { SomeThing } from "./some-module.js";

export type { SomeThing };
```

### 注意点
これでインポートされるのは純粋に型の意味のみなので、値が使用されていてもその値は使用することができません。

```typescript
import type { Hoge } from './foo';

let baseConstructor: typeof Hoge;
//                          ~~~~ Error!!
```

これに対してのフィードバックをうけて、この動作を変えることを検討しているようです。
まだ計画段階らしいですが、、`import type`を使用してインポートされたものは、型でもそれ以外であっても常に型の位置でのみ許可されるように変える可能性もあると。なんにしても公式のアナウンスやアップデートを待ったほうがよさそうですね。

### importsNotUsedAsValuesオプションの追加

使用されていないインポートがなされていた時の処理を制御するコンパイラフラグです。
設定できる値は以下の通り

- remove(default）
現在の動作と同じ。使用されない要素はドロップされます

- preserve
使用されないインポートも保持されるようになります。

- error
すべてのインポートが保持される点は`preserve`と同じですが、値のインポートが型として使用される場合、エラーとなります。
 
## ECMAScriptプライベートフィールド

ECMAScript2015以降で使えるプライベートフィールドがサポートされました。
プライベートフィールドはクラス内でのみ参照可能なフィールドで、クラス外から参照しようとすると怒られます。
`#hoge = 1`のように接頭辞に`#`をつけることで宣言できます。

```typescript
class Person {
    #name: string
    
    constructor(name: string) {
        this.#name = name;
    }

    greet() {
        console.log(`Hello, my name is ${this.#name}!`);
    }
}

let jeremy = new Person("Jeremy Bearimy");

jeremy.#name // Error! クラス外部からの参照を許さない
```
privateとこの記述のどちらを使うべきかは状況によりそうです。
例えばECMAScript 2015(ES6)以上がターゲットでない場合、そもそもプライベートフィールド機能自体が使用できません。

## `export * as ns` Syntax

`import * as ~`のような記述がexportでもできるようになりました。 


```typescript
export * as utilities from "./utilities.js";
```

## Top-Level awaitのサポート

ECMAScriptの機能「Top-Level await」に対応しました。
`await`を使用するには`async`をつける必要がありましが、この「Top-Level await」を用いると、モジュールのトップであるときのみasyncがなくてもawaitを使うことができます。

```typescript
const response = await fetch("...");
const greeting = await response.text();
console.log(greeting);

// Make sure we're a module
export {};
```

※ moduleの設定を`system`もしくは`esnext`、targetの設定をes2017以上に設定してあげる必要があります。

## JSDoc Property Modifiers

allowJsフラグをonにすることでjsファイルをサポート、かつchackJs,、もしくは@ts-checkコメントを先頭につけることで.jsファイルの型チェックもサポートします。

`@public`, `@private`, and `@protected`修飾子があります。

```typescript
// @ ts-check 
class Foo {
    constructor() {
        /** @private */
        this.stuff = 100;
    }

    printStuff() {
        console.log(this.stuff);
    }
}

new Foo().stuff; // Error!!
```

- `@public`
プロパティに対してどこからでもアクセスできることを示します。省略可
- `@private`
プロパティがクラス内からのみ使用できることを示します。
- `@protected`
プロパティはクラス内と派生サブクラス内でのみ使用でき、宣言されてあクラスの異なるインスタンスでは使用できないことを示します。

また、初期化中にのみ書き込まれるようにする修飾子`@readonly`も追加されました。

```typescript
// @ts-check

class Foo {
    constructor() {
        /** @readonly */
        this.stuff = 100;
    }

    writeToStuff() {
        this.stuff = 200;
        //   ~~~~~
        // Cannot assign to 'stuff' because it is a read-only property.
    }
}

new Foo().stuff++; // Error!!
```

## watchOption

ファイル監視のオプションが追加されました。

```typescript
{
    // いつもの
    "compilerOptions": {
        "target": "es2020",
        "moduleResolution": "node",
        // ...
    },

    // NEW: あたらしく追加された「watchOptions」
    "watchOptions": {
        // 
        "watchFile": "useFsEvents",
        "watchDirectory": "useFsEvents",

        // ファイルが頻繁に更新されるとき
        "fallbackPolling": "dynamicPriority"
    }
}
```

これには４つのオプションが含まれます。

- WatchFile   
個々のファイルを監視する方法を設定します。
  - fixedPollingInterval   
  一定の間隔で毎秒数回、すべてのファイルの変更を確認します。
  - priorityPollingInterval   
  すべてのファイルの変更を1秒間に数回チェックしますが、ヒューリスティックを用い、特定のタイプのファイルを他のファイルよりも頻繁にチェックしなくなります。
  - dynamicPriorityPolling   
  変更頻度の少ないファイルのチェック頻度が低い動的キューを使用します。
  - useFsEvents (default)   
  ファイルの変更にオペレーティングシステム/ファイルシステムのネイティブイベントを使用します。
  - useFsEventsOnParentDirectory   
  オペレーティングシステム/ファイルシステムのネイティブイベントを使用して、ファイルを含むディレクトリの変更を検知します。これにより使用するファイルウォッチャーは少なくなりますが、制度が低下してしまう恐れがあります。
- watchDirectory
  再帰的なファイル監視機能がないシステムでディレクトリツリー全体を監視する方法を設定します。
  - fixedPollingInterval   
  すべてのディレクトリの変更を、一定の間隔で1秒間に数回確認します。
  - dynamicPriorityPolling   
  変更頻度の低いディレクトリのチェック頻度が低い動的キューを使用します。
  - useFsEvents (default)
  ディレクトリの変更にオペレーティングシステム/ファイルシステムのネイティブイベントを使用します。
- fallbackPolling
  ファイルシステムイベントを使用する際、システムがネイティブファイルウォッチャーを使い果たしたり、サポートしていないときに使用するポーリングの設定を指定します。
  - fixedPollingInterval   
  - dynamicPriorityPolling   
  - useFsEvents (default)
- synchronousWatchDirectory   
ディレクトリの遅延監視を無効にします。

## assumeChangesOnlyAffectDirectDependenciesオプションの追加

長！！！ 
--incrementalモードに関連したコンパイルオプションです。
公式では*“Fast and Loose” Incremental Checking*「"高速で緩い"インクリメンタルチェック」と紹介されています。
assumeChangesOnlyAffectDirectDependenciesオプションを有効にすると影響を受ける可能性のあるすべてのファイルの再チェック/再構築を回避し、変更されたファイルと直接インポートするファイルのみを再チェック/再構築するようになります。

VSCodeのようなコードベースでは、このオプションによりファイル変更の再構築時間が14秒から1秒まで短縮されたようです。（自分でも検証してみます）

## TypeScript3.8での変更

### インデックスシグネチャを使用したユニオン型への厳密な割り当てチェック

以前は、存在しないキーに対してはコンパイルチェックが効かないようになっていました。
ver3.8では、プロパティがインデックスシグネチャを満たす場合のみ、プロパティチェックから除外します。
例えば以下のコードは従来であればエラーは起きませんでしたが、3.8ではエラーになります。

```typescript
let obj1: { [x: string]: number } | { a: number };

obj1 = { a: 5, c: 'abc' } // cへの代入はインデックスシグネチャを満たさないためエラー
```

--- 
自分で触ってみて感じたことなど追記したり、情報が間違っていたら修正するので随時更新するつもりです。

またフィードバックを募集しているらしいので、触ってみた方は気になる点を報告するといいかもしれません。

## 参考
https://devblogs.microsoft.com/typescript/announcing-typescript-3-8-beta/