Catch up: Announcing TypeScript 4.4 Beta
TypeScript 4.4 betaがアナウンスされたので中身を読んで概要をつまんでいくスクラップ。
Control Flow Analysis of Aliased Conditions
argがunknownの時、if文でようやくstringと区別できる。
function foo(arg: unknown) {
if (typeof arg === "string") {
// We know this is a string now.
console.log(arg.toUpperCase());
}
}
4.3以前だと以下のように許してもらえなかったが、4.4ではargIsStringでチェックした情報が失われずに済むようになる。
function foo(arg: unknown) {
const argIsString = typeof arg === "string";
if (argIsString) {
console.log(arg.toUpperCase());
// ~~~~~~~~~~~
// Error! Property 'toUpperCase' does not exist on type 'unknown'.
}
}
こういうのもいけちゃう。
type Shape =
| { kind: "circle", radius: number }
| { kind: "square", sideLength: number };
function area(shape: Shape): number {
const isCircle = shape.kind === "circle";
if (isCircle) {
// 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;
}
}
typeofでチェックせずにこんなこともOK
function doSomeChecks(
inputA: string | undefined,
inputB: string | undefined,
shouldDoExtraWork: boolean,
) {
let mustDoWork = inputA && inputB && shouldDoExtraWork;
if (mustDoWork) {
// Can access 'string' properties on both 'inputA' and 'inputB'!
const upperA = inputA.toUpperCase();
const upperB = inputB.toUpperCase();
// ...
}
}
こんなことも可能。
function f(x: string | number | boolean) {
const isString = typeof x === "string";
const isNumber = typeof x === "number";
const isStringOrNumber = isString || isNumber;
if (isStringOrNumber) {
x; // Type of 'x' is 'string | number'.
}
else {
x; // Type of 'x' is 'boolean'.
}
}
Symbol and Template String Pattern Index Signatures
これまでのTypeScriptではindex signatureにはstringかnumberしか許可されてませんでした。
4.4からは、symbolとtemplate string patternがサポートされます。
type Colors = {
[sym: symbol]: number;
}
const red = Symbol("red");
let colors: Colors = {};
colors[red] = 255; // Assignment of a number is allowed
type Option = {
[optName: `data-${string}`]: unknown;
}
let b: Option = {
"data-blah": true, // Works!
"unknown-property": true, // Error! 'unknown-property' wasn't declared in 'Option'.
};
unionも許可。Data1とData2は同等
interface Data1 {
[optName: string | symbol]: any;
}
interface Data2 {
[optName: string]: any;
[optName: symbol]: any;
}
Defaulting to the unknown Type in Catch Variables (--useUnknownInCatchVariables)
これまで、catch(err) のerrはanyだったので以下のようなコードが書けていた。
try {
// Who knows what this might throw...
executeSomeThirdPartyCode();
}
catch (err) { // err: any
console.error(err.message); // Allowed, because 'any'
err.thisWillProbablyFail(); // Allowed, because 'any' :(
}
そこでより厳格にするためにはanyではなくunknownとすると以下のように出来る。
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);
}
}
4.4では、--useUnknownInCatchVariablesフラグを導入することでcatch内のerrをanyからunknownに切り替えることが可能に。あ、デフォルトでオンになる。やめたかったらオフへ。
ちなみに、--strict をつけていると自動で--useUnknownInCatchVariablesはオンになる。
Exact Optional Property Types (--exactOptionalPropertyTypes)
これは
interface Person {
name: string,
age?: number;
}
こうみなされ
interface Person {
name: string,
age?: number | undefined;
}
現状、TSは、プロパティが 無い
のか undefinedを与えられている
のか区別していない。
const p: Person = {
name: "Daniel",
age: undefined, // This is okay by default.
};
4.4では、--exactOptionalPropertyTypesフラグを導入し、これをオンにするとoptional property(上記 age?: number)なものに明示的にundefinedが与えられるとエラーに出来る。
// With 'exactOptionalPropertyTypes' on:
const p: Person = {
name: "Daniel",
age: undefined, // Error! undefined isn't a number
};
こっちは--strictには含まれないので注意。