「Announcing TypeScript 4.8」を読む
この記事を知った翌日に4.8がリリースされていたので、勉強を兼ねて自分なりに読んでまとめてみようと思います。
本記事は、次のページの内容を自分なりにまとめたものです。
https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/
Improved Intersection Reduction, Union Compatibility, and Narrowing
{}
型関係の挙動が改善されました。
-
{} | null | undefined
型の変数にunknown
型の変数が代入できるようになりました。
function test(a: unknown, b: {} | null | undefined){
a = b; // 以前からOK。unknownはanyと同じでなんでも代入できる。
b = a; // 以前はNG、4.8からOK
}
-
{}
型とobject typeのintersection(交差型)が、単に相手のobject typeに推論されるようになりました。(*1)
これにより、NonNullable<T>
型の実装が変わっています。
type NonNullable<T> = T extends null | undefined ? never : T; // ~4.7
type NonNullable<T> = T & {}; // 4.8~
-
unknown
型変数をif文でチェックした場合、true branchのほうは{}
型にnarrowingされるようになりました。
function test(a: unknown){
if (a) {
a; // 前はunknown型、4.8は{}型
} else {
a; // unknown型。
}
}
- Generic型についても同様に、
null
とundefined
のいずれでもないと推定される変数は、{}
との交差型と推定されます。
function test<T>(a: T): NonNullable<T> {
if (a === null || a === undefined) {
throw Error();
}
// 以前はT型と推論されるため、戻り値 NonNullable<T> 型と合わず型エラーとなる。
// 4.8以降はT & {}型に推論されるため、型エラーにならない。
return a;
}
(*1: 書き方を見るに、null,undefined以外の型をobject typeというらしいです。)
Improved Inference for infer Types in Template String Types
Template文字列型の中でinfer~extendsを使った時、可能であれば具体的なリテラル型に変換されるようになりました。
ただし、'1.0'
というリテラル型にはうまくはまらないそうです。元の文字列をプリミティブ値に変換して、もう一度文字列に戻した時に、元の文字列と等しくないと、リテラル型にならないそうです。'1.0'
型は、数値型にすると1
型になるので、それを文字列化しても元と一致しないということですね。
いくつかテストしてみました。動作は以下のようです。
type Test<T> = T extends `${infer U extends number}` ? U : never;
type T1 = Test<'0'>; // 0
type T2 = Test<'0' | '4'>; // 0 | 4
type T3 = Test<'-4'>; // -4
type T4 = Test<'0' | '4' | 'number'>; // 0 | 4
type T5 = Test<'1.0'>; // number
type T6 = Test<'NaN'> // never
type T7 = Test<'Infinity'> // never
Errors When Comparing Object and Array Literals
==
と===
の比較でオブジェクトや配列のリテラルを使うと、エラーとなります。
JavaScriptはオブジェクト・配列の比較が参照比較なので、リテラルで今作ったオブジェクトと変数の参照が一致することはなく、必ずfalseになるif文が書けなくなります。これはいいですね!
if (someArray === []) foo();
// ~~~~~~~~~~~~~~~~~ エラーになる。
Improved Inference from Binding Patterns
パッと読んで良く分からなかったので、テストコードを書いてみました。
const sample = <T>(a: T, b: T): T => a;
const [a, b, c] = sample([0, 'a', true], [1, 'b', false]); // 4.8以降もOK
const sample2 = <T>(t?: T): T => {
if (!t) throw new Error();
return t;
}
const [d, e, f] = sample2(); // 4.7はOK、4.8ではNG。
// ~~~~~~~ ←ここの型推論が変わる。
それぞれの型推論を見てみると、[d, e, f] = sample2();
としているところのsample2の型が、4.7では<[any, any, any]>(t?: [any, any, any] | undefined) => [any, any, any]
型、4.8では<unknown>(t?: unknown) => unknown
型と推論されます。
今までは、[d,e,f]
に分割代入しているということが優先度の低い型推論として型パラメータT
を[any, any, any]
と推論していましたが、それがなくなると書いてあります。こんなケースに遭遇するかは分かりませんが、微妙な場合はちゃんと型変数を明示したほうがよさそうですね。
Exclude Specific Files from Auto-Imports
エディターに設定を追加すると、特定のファイルの型を除外できるようになりました。コンパイルには必要だけど、importはしないファイルとかを設定しておくらしいです。設定方法はリンク先にあるので、本記事では割愛します。
lib.d.ts Updates
いくつか変更があったそうですが、ノートにはError.cause
の型がError
からunknown
に変更されたことが一つの目玉として紹介されています。
Unconstrained Generics No Longer Assignable to {}
何も判定していないGeneric Typeは、{}
型に代入できなくなります。
function test<T>(t: T) {
const obj: {} = t; // 今まではOK、これからはNG。
if (t === null || t === undefined) return;
const obj2: {} = t; // 今まで通り今後もOK
}
Decorators are placed on modifiers on TypeScript’s Syntax Trees
少し調べてみましたが、そもそもDecoratorの知識があやふやな状態なので分かりませんでした!調べたことをメモしておくので、コメントで補足いただけるとうれしいです。
- JavaScriptでのDecoratorの仕様が固まってきたため、それに合わせてTypeScriptの内部実装を調整する話っぽい。
-
ModifierLike
という型が記事中にあるが、これはTypeScript内部で使う型で、TypeScriptで開発者する人が使う型ではないっぽい。(たぶん) - 別に今まで通り
export
の前にデコレーターを書いても問題なく使えそう。
Binding Patterns Do Not Directly Contribute to Inference Candidates
上述の「Improved Inference from Binding Patterns」と同じ内容です。
その他
上記以外にも項目がありますが、リンクだけまとめます。