Closed5

Catch up: Announcing TypeScript 4.4 Beta

Teruhisa - T6ADEVTeruhisa - T6ADEV

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'.
    }
}
Teruhisa - T6ADEVTeruhisa - T6ADEV

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;
}
Teruhisa - T6ADEVTeruhisa - T6ADEV

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はオンになる。

Teruhisa - T6ADEVTeruhisa - T6ADEV

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には含まれないので注意。

このスクラップは2021/07/02にクローズされました