TypeScript 3.4 RCの変更点まとめ

本日、TypeScript 3.4 RCがアナウンスされました。
[Announcing TypeScript 3.4 RC](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4-rc/)

# まとめ
- ビルド時間の短縮のための--incrementalフラグが追加
- Readonly周りの改善
  - readonly修飾子でReadonlyArray型を簡易に表記可能に
  - readonlyタプルの追加
  - readonly修飾子付きのMappedTypesの自動変換機能追加
- const assertion構文の追加
- globalThisの型チェック追加
- named parametersに変換するためのリファクタ機能追加
- 破壊的変更
  - トップレベルのthisの型がanyからglobalThisに変更
  - 関数の型推論改善による影響

# --incrementalフラグ
--watchモードでは、前回コンパイル時の状態を元に影響範囲のみの型チェックが行われます。
v3.4で追加される--incrementalフラグを付与すると、--watchモードではない通常のコンパイル時にも、
前回コンパイル時の状態が保存され、ビルド時間の短縮が見込めます。
前回状態はデフォルトでは`./lib/.tsbuildinfo`に保存されます。(こちらも--tsBuildInfoFileで変更可能)

# Readonly周りの改善
## ReadonlyArray型の簡易表記
通常の配列は`number[]`と`Array<number>`などの２種類の表記方法があります。
ReadonlyArray型に関しては、`Readonly<number>`とジェネリクスを使った表記しかありませんでしたが、
v3.4から、`readonly number[]`と、通常の配列のようなショートハンドな表記が可能となりました。

## readonlyタプル
上記と同様の構文で、v3.4から再代入不能なタプルの型も表現可能となりました。

```javascript
const readonlyTuple: readonly [string, number] = ['hoge', 1]
// error
readonlyTuple[0] = 'fuga'
```

## readonly修飾子付きMappedTypesの自動変換
MappedTypesにreadonly修飾子を付与したイディオムにおいて、
object型ではなく配列やタプルをジェネリクスの型として定義した場合の挙動がv3.4で変更になりました。
自動で上記2つの型に変換してくれるようです。

```javascript
// lib.d.ts
type Readonly<T> = {
    readonly [K in keyof T]: T[K]
}

// before v3.4
// type B is number[]
type B = Readonly<number[]>

// type C is [string, boolean]
type C = Readonly<[string, boolean]>

// v3.4
// type B is readonly number[] (ReadonlyArray<number>)
type B = Readonly<number[]>

// type C is readonly [string, boolean]
type C = Readonly<[string, boolean]>c
```

逆に、readonlyを取り除くMappedTypesに関しても、
以下のような自動変換が働きます。

```javascript
// lib.d.ts
type Writable<T> = {
    -readonly [K in keyof T]: T[K]
}

// number[]
type B = Writable<readonly number[]>;

// [string, boolean]
type C = Writable<readonly [string, boolean]>;
```

## constアサーション
v3.4から`~ as const`という構文が追加されました。

まず、下記の場合
'hello'というリテラル自体は'hello'型ですが、
letで宣言しているので、再代入を可能にするためstring型に推論されます。

```javascript
// Type string
let x = 'hello'
x = 'world'
```

次に、constアサーションを用いた場合、
上記と異なりstring型ではなく'hello'型として推論されるほか、

- 配列リテラル -> readonlyタプル
- オプジェクトリテラル -> プロパティがreadonlyのオブジェクト

にそれぞれ推論されます。

```javascript
// Type 'hello'
let x = 'hello' as const

// Type 'readonly [10, 20]'
let y = [10, 20] as const

// Type '{ readonly text: "hello" }'
let z = { text: "hello" } as const
```
※ *.tsx以外のファイルでは、`let x = <const>'hello'`のような構文も可能

また、constアサーションはリテラルに対してのみしか使用できないようです。

```javascript
// error
let a = (Math.random() < 0.5 ? 0 : 1) as const
```

# globalThisのサポート
ECMAScriptのプロポーザルである[globalThis](https://github.com/tc39/proposal-global)(2019/3/16現在 Stage3)に関して、
v3.4から型チェックのサポートが追加されました。

```javascript
// in a global file
const hoge = 'hoge'

// error
globalThis.hoge = 'fuga'
```

ただし、TypeScriptはglobalThisを変換しないため、
サポートしていないブラウザで動かすにはpolyfillを入れる必要があります。

# named parametersへのリファクタ機能
関数の引数が増えてきて、named parameterに変換したい場合のリファクタ機能が追加されました。
以下のgifを見ていただければ分かると思います。

> ※ [アナウンスページ](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4-rc/)から画像を借用
 ![68747470733a2f2f646576626c6f67732e6d6963726f736f66742e636f6d2f747970657363726970742f77702d636f6e74656e742f75706c6f6164732f73697465732f31312f323031392f30332f7265666163746f72546f4e616d6564506172616d734f626a6563742d332e342e676966.gif](https://qiita-image-store.s3.amazonaws.com/0/171032/0f618a34-2f32-9f2e-eebe-d232f7ce11e0.gif)


# 破壊的変更
以下の2点がアナウンスされていますが、
以前はコンパイル通っていたが、コンパイルエラーとなるケースがあるだけで、
コンパイル後のコードが変わることはないようです。

## トップレベルのthisの型
トップレベルのthisの型はany型でしたが、
v3.4からglobalThis型となります。

以前はコンパイルが通っていた以下のコードも、
v3.4では、noImplicitAnyオプションが有効な場合、
globalThis型に定義のないhogeプロパティは暗黙のany型としてエラーとなります。

```javascript
this.hoge = 'hoge'
```

## 関数の型推論改善による影響
関数の型推論が改善され、コンパイルエラーとなるケースがあるそうです。

例えば、以下のコードの場合、
x.valueは、以前は`{}[]`型と推論されましたが、
v3.4からは`number[]`型と推論されるようになります。
従って、x.valueにnumber型以外をpushするようなコードが型エラーとなるようです。

```javascript
declare function compose<T, U, V>(f: (arg: T) => U, g: (arg: U) => V): (arg: T) => V;

function list<T>(x: T) { return [x]; }
function box<T>(value: T) { return { value }; }

let f = compose(list, box);
let x = f(100)
```