先日、TypeScript 4.4のRCバージョンについてのアナウンスがありました。
Today we’re excited to announce our Release Candidate (RC) of TypeScript 4.4! Between now and the stable release of TypeScript 4.4, we expect no further changes apart from critical bug fixes.
そこで、本日はアナウンスの内容で気になった以下の4つを紹介したいと思います。
- Control Flow Analysis of Aliased Conditions and Discriminants
- Symbol and Template String Pattern Index Signatures
- Defaulting to the unknown Type in Catch Variables (--useUnknownInCatchVariables)
- static Blocks in Classes
#1.Control Flow Analysis of Aliased Conditions and Discriminants
こちらは、Type Guardを今までよりも柔軟に使用するための変更です。
v4.3以前
function foo(arg: unknown) {
const argIsString = typeof arg === "string";
if (argIsString) {
console.log(arg.toUpperCase());
// ~~~~~~~~~~~
// Error! Property 'toUpperCase' does not exist on type 'unknown'.
}
}
argIsString
が呼び出される段階でType Guardとしての機能が失われしまうため、errorが発生します。
v4.4
からは、条件式の変数が評価される段階で、その変数にType Guardが含まれるかどうかを確認してくれるため、上記のようなerrorは発生しません。
これによって、一つの関数内で同じType Guardを何度も記載する必要がなくなります。
また、プロパティの値を用いてType Guardを実現できるようにもなります。
Enum
と組み合わせれば便利な気がします。
type Shape =
| { kind: "circle", radius: number }
| { kind: "square", sideLength: number };
function area(shape: Shape): number {
// Extract out the 'kind' field first.
const { kind } = shape;
if (kind === "circle") {
// We know we have a circle here!
return Math.PI * shape.radius ** 2;
}
else {
// We know we're left with a square here!
return shape.sideLength ** 2;
}
}
ただし、以下のように関数の引数に分割代入が使用されている場合、Type Guardは機能しないので注意が必要です。
type Data = { kind: 'str', payload: string } | { kind: 'num', payload: number };
function foo({ kind, payload }: Data) {
if (kind === 'str') {
payload.length; // Error, payload not narrowed to string
}
}
#2. Symbol and Template String Pattern Index Signatures
objectのindex signaturesにSymbolやテンプレート文字列を使用可能になります。
-
Symbol
の場合
object
のプロパティは常に衝突する危険性を孕んでいるので、object
を一意に特定するためのidなどはSymbol
をkeyとしたプロパティに持たせておくのが良いかもしれないです。
※Symbol
をkeyとしたプロパティは、JSON.stringfy()
やObject.keys()
で出力されないので注意
interface Colors {
[sym: symbol]: number;
}
const red = Symbol("red");
const green = Symbol("green");
const blue = Symbol("blue");
let colors: Colors = {};
colors[red] = 255; // Assignment of a number is allowed
let redVal = colors[red]; // 'redVal' has the type 'number'
colors[blue] = "da ba dee";
-
テンプレート文字列
の場合
keyが特定のパターンで作成されている場合などに便利
interface Options {
width?: number;
height?: number;
}
let a: Options = {
width: 100,
height: 100,
"data-blah": true, // Error! 'data-blah' wasn't declared in 'Options'.
};
interface OptionsWithDataProps extends Options {
// Permit any property starting with 'data-'.
[key: `data-${string}`]: boolean | number;
}
let b: OptionsWithDataProps = {
width: 100,
height: 100,
"data-blah": true, // Works!
"data-num": 200, // Works!
"unknown-property": true, // Error! 'unknown-property' wasn't declared in 'OptionsWithDataProps'.
};
#3.Defaulting to the unknown Type in Catch Variables (--useUnknownInCatchVariables)
tsconfig.json
の設定から、catchするerrorの型をデフォルトでunknown
に指定することができるようになります。
これによって、catch句の中でerrorの型定義が可能となり、今までよりも型安全性が保たれます。
try {
executeSomeThirdPartyCode();
}
catch (err) { // err: unknown
// Error! Property 'message' does not exist on type 'unknown'.
console.error(err.message);
// Works! We can narrow 'err' from 'unknown' to 'Error'.
if (err instanceof Error) {
console.error(err.message);
}
}
※ 今までもerrorにunknownを指定することはできましたが、毎回catch (err: unknown)
と記載する必要がありました。
4.static Blocks in Classes
クラス内でstatic
なブロックが使用可能になり、static
な変数を設定する際に複雑な処理を記載しやすくなります。
※ static BlocksはECMAScriptの仕様策定におけるStage3です。(2021/08/15時点)
class Foo {
static Foo.count = 0;
// This is a static block:
static {
if (someCondition()) {
Foo.count++;
}
}
}
参照
https://devblogs.microsoft.com/typescript/announcing-typescript-4-4-rc/
https://sosukesuzuki.dev/posts/stage-3-class-static-blocks/