「Announcing TypeScript 4.9 Beta」を読む

# 「Announcing TypeScript 4.9 Beta」を読む

[前回](https://qiita.com/charon1212/items/c37d5d38b35b801bd52e)に引き続き、[Announcing TypeScript 4.9 Beta](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/)を読んでいきます。
（関心が続く限り続けていきたいです。。。）

## [The satisfies Operator](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#the-satisfies-operator)

新しく「型推論に影響せず、型チェックのみを行う」演算子が追加されました。
以下の例は、`Record<string, number | number[]>`型に代入可能であることをチェックしますが、その結果は`{hoge: number, fuga: number[]}`型に推論されます。

```ts
const sample = {
  hoge: 3,
  fuga: [2, 4, 6],
} satisfies Record<string, number | number[]>;

sample.fuga[2]; // 4
```

上記の例は、sampleに`Record<string, number | number[]>`型を付与してしまうと、`sample.fuga`が`number`型の可能性があるため、エラーとなります。

```ts
const sample: Record<string, number | number[]> = {
  hoge: 3,
  fuga: [2, 4, 6],
};

sample.fuga[2]; // ERROR
```

これがどの程度有用なのかは分からないのですが…個人的には代入の時よりも、ある変数がある型になっていることをチェックするだけのために使うと役立ちそうだな～と思いました。
例えば、パターンマッチングで、網羅性を調べられます。ライブラリを入れずともできるので、手軽ですね。

```ts
const sample = (a: 'b' | 'c' | 'd') => {
  if (a === 'b') return 1;
  if (a === 'c') return 2;
  a satisfies never; // ERROR
};
```

後、関数の関数を呼び出した時とかもチェックできます。

```ts
const sample = (f: (a: string) => (b: number) => void) => {
  f('hoge')(3) satisfies void;
  f('hoge') satisfies void; // ERROR
};
```

あんまりなさそうですが…「あれ？これでいいっけ？」と思った時に書いてもいいかもしれません。
ただし、書いた瞬間は多少満足感があったのですが、落ち着いてみるとコードが汚くなり、可読性は落ちそうに感じてしまいます。。。

一応、`await`忘れ対策もできるっちゃできます。

```ts
const asyncFunc = async () => {};
const sample = async () => {
  await asyncFunc() satisfies void;
  asyncFunc() satisfies void; // ERROR
};
```

けど、`await`忘れるなら`satisfies`をつけ忘れそうです。現実的ではないですかね。。。

## [Unlisted Property Narrowing with the in Operator](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#unlisted-property-narrowing-with-the-in-operator)

明示的にリストアップされていないPropertyに対しても、`in`演算子の絞り込みでNarrowingするようになりました。（今までできなかったんだ。。。）

以下のように、明示的にPropertyが宣言されていれば、従来から`in`演算子で絞り込みが効いていました。

```ts
const sample = (a: {b: string} | {c: number}) => {
  if ('b' in a){
    a; // {b: string}
    a.b; // string
  }
};
```

明示的に宣言されていないと、Narrowingされませんでしたが、それが`object & Record<"b", unknown>`型にNarrowingされます。

```ts
const sample = (a: object) => {
  if ('b' in a){
    a.b; // 4.8まではエラー
  }
};
```

## [Checks For Equality on NaN](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#checks-for-equality-on-nan)

バグになりやすい、NaNとの等値比較をエラー扱いするようになりました。ちゃんとisNaNを使おうと注意してくれます。

```ts
const sample = (a: number) => {
  if (a === NaN) return undefined; // ERROR
  return a + 1;
};
```

## [File-Watching Now Uses File System Events](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#file-watching-now-uses-file-system-events)

ファイルの変更を監視する仕組みが、ポーリングからOSのファイルシステムイベントになるそうです。それまでの経緯などが書かれていました。

## [Better Types for Promise.resolve](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#better-types-for-promise-resolve)

`Promise.resolve`の戻り値型が変わりました。
いくつか試したところ、以下のコードで差が出ました。
以前は`T | PromiseLike<T>`を受け取って`Promise<T>`を返却していましたが、ちゃんと`Awaited`でPromiseを解決するようにしたみたいです。
自分が使っている限りでは困りませんが、ライブラリ開発者の方とかは気にする必要があるのかもしれないですね～

```ts
const sample = () => {
  // 4.8     ではPromise<Promise<unknonw>>型
  // 4.9beta ではPromise<unknonw>型
  const a = Promise.resolve({} as Promise<Promise<unknown>>);
};
```

## その他

上記以外にも項目がありますが、リンクだけまとめます。

- [lib.d.ts Updates](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#lib-d-ts-updates)
- [JavaScript Emit No Longer Elides Imports](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#javascript-emit-no-longer-elides-imports)
- [exports is Prioritized Over typesVersions](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#exports-is-prioritized-over-typesversions)
- [substitute Replaced With constraint on SubstitutionTypes](https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#substitute-replaced-with-constraint-on-substitutiontypes)