[TypeScript] 3.2¶
TypeScript3.2のリリース内容まとめ
strictBindCallApply¶
HAD BETTER EASY
--strictBindCallApply
オプションが有効なとき、以下3つのメソッドの型チェックを厳密に行うようになった。
--strict
が有効であれば--strictBindCallApply
を個別に指定する必要はない。
- bind
- call
- apply
function foo(a: number, b: string): string {
return a + b;
}
// 正しい
foo.apply(undefined, [10, "hello"]);
// -> 10hello
// requiredの第2引数が指定されていない (けどエラーにならない..!!)
foo.apply(undefined, [10]);
// -> NaN
// 第2引数の型が違う (けどエラーにならない..!!)
foo.apply(undefined, [10, 20]);
// -> 30
// 不要な第3引数が含まれている (けどエラーにならない..!!)
foo.apply(undefined, [10, "hello", 30]);
// -> 10hello
function foo(a: number, b: string): string {
return a + b;
}
// 正しい
foo.apply(undefined, [10, "hello"]);
// -> 10hello
// requiredの第2引数が指定されていない (のでERRORになる)
foo.apply(undefined, [10]);
// index.ts:5:30 - error TS2345: Argument of type '[number]' is not assignable to parameter of type '[number, string]'.
// Property '1' is missing in type '[number]' but required in type '[number, string]'.
// 第2引数の型が違う (のでERRORになる)
foo.apply(undefined, [10, 20]);
// index.ts:6:35 - error TS2322: Type 'number' is not assignable to type 'string'.
// 不要な第3引数が含まれている (のでERRORになる)
foo.apply(undefined, [10, "hello", 30]);
// index.ts:7:30 - error TS2345: Argument of type '[number, string, number]' is not assignable to parameter of type '[number, string]'.
// Types of property 'length' are incompatible.
// Type '3' is not assignable to type '2'.
Caveats¶
HAD BETTER EASY
strictBindCallApplyは以前エラーでなかった箇所がエラーになるため、破壊的変更と言える。
また、bind
call
apply
は以下のケースで完全に正しくモデル化できないので注意。
- ジェネリックな関数
- オーバーロードされた関数
Generic spread expressions in object literals¶
HAD BETTER EASY
ジェネリクス型でもspread構文が使えるようになった。
エラーになります。
interface Human {
id: number;
name: string;
}
function mergeWithNumber<T>(obj: T, num: number) {
return { ...obj, number: num }; // error TS2698: Spread types may only be created from object types.
}
const taro: Human = { id: 1, name: "taro" };
const jiro = mergeWithNumber(taro, 77);
利用可能で型推論もできます。
interface Human {
id: number;
name: string;
}
function mergeWithNumber<T>(obj: T, num: number) {
return { ...obj, number: num };
}
const taro: Human = { id: 1, name: "taro" };
const jiro = mergeWithNumber(taro, 77);
// taroの型 と number の Intersection typesになる
// jiro: Human & {
// number: number;
// }
Generic object rest variables and parameters¶
HAD BETTER EASY
ジェネリクス型でもobjectのrest parametersが使えるようになった。
エラーになります。
interface Human {
id: number;
name: string;
isGod: boolean;
}
function showWithoutId<T extends { id: number }>(obj: T) {
const { id, ...rest } = obj; // error TS2698: Spread types may only be created from object types.
console.log(rest);
}
const kbys: Human = { id: 1, name: "yshr", isGod: true };
showWithoutId(kbys);
利用可能で型推論もできます。
interface Human {
id: number;
name: string;
isGod: boolean;
}
function showWithoutId<T extends { id: number }>(obj: T) {
const { id, ...rest } = obj;
console.log(rest); // rest: Pick<T, Exclude<keyof T, "id">> と推論
}
const kbys: Human = { id: 1, name: "yshr", isGod: true };
showWithoutId(kbys);
// -> { name: 'yshr', isGod: true }
BigInt¶
NOT NECESSARY EASY
number
よりも大きな数で精度が保てる型bigint
が追加された。
// number
console.log(12345678901234567890);
// -> 12345678901234567000
// 最後の890で精度が保たれていない
// bigint (末尾にnをつけるか BigInt(...)で作る)
console.log(12345678901234567890n);
// -> 12345678901234567890n
// 精度が保たれる
number
とbigint
は相互に代入/計算できないためキャストが必要。
また、利用にはtarget: esnext
が必要。 (ES2020以降)
Non-unit types as union discriminants¶
HAD BETTER EASY
Union typesの各typeがシングルトン(固定文字列のtagなど)以外のプロパティを持つ場合でもnarrowingができるようになった。
type Result<T> = { error: Error; data: null } | { error: null; data: T };
function unwrap<T>(result: Result<T>) {
if (result.error) {
// 3.1も3.2以降も `Error`型
throw result.error;
}
// 3.1以前は `T | null`型
// 3.2以降は `T`型
return result.data;
}
result.error
はT
またはnull
型だが、T
はシングルトンでないため以前はnarrowingできなかった。
v3.2からはそれが可能になったため、if (result.error)
の条件ブロックに入らなかった時点でResult<T>
型は{ error: null; data: T }
型であると判断できるようになった。
tsconfig.json inheritance via Node.js packages¶
HAD BETTER EASY
node_modules
からtsconfig.json
を継承できるようになった。
オレオレtsconfig.json
をpackage化してextends
に指定できる。
extends
は Configuration inheritance を参照
The new --showConfig flag¶
HAD BETTER EASY
tsc
コマンドに--showConfig
が追加された。
このオプションをつけると、コマンド実行時に有効と判定されたtsconfig.json
の設定が出力される。
$ npx tsc --showConfig
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"./main.ts"
]
}
特にextends
を使った時は便利。
{
"compilerOptions": {
"target": "esnext"
},
"extends": "./parent-tsconfig.json"
}
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true
}
}
target
だけが上書き/マージされた結果を取得できる。
$ npx tsc --showConfig
{
"compilerOptions": {
"target": "esnext",
"module": "commonjs",
"strict": true
},
"files": [
"./main.ts"
]
}
Object.defineProperty declarations in JavaScript¶
NOT NECESSARY EASY
JavaScriptファイルで// @ts-check
を使用したとき、Object.defineProperty
の処理が考慮されるようになった。
--allowJs
を有効にした上で以下のファイルを作成してみる。
// @ts-check
let ichiro = {};
Object.defineProperty(ichiro, "name", { value: "Ichiro" });
ichiro.name.toLowerCase();
// -> ichiro を期待
v3.1以前とv3.2では以下のように結果が異なる。
ichiro
にname
プロパティがあると認識されない。
ichiro
にname
プロパティがあると認識される。