Typescript 4.4 is out! Typescript 4.5 is three months away, so get on board and learn!

Intensive reading of the week: announcing-typescript-4-4

An overview of the

More intelligent automatic type narrowing

Type narrowing is a handy feature that lets Typescript automatically and intelligently determine types as much as Js does, avoiding type definition and making your Typescript write more like Js.

We’ve already seen this feature in typescript2.0-2.9. The term automatic type inference is used at the time. This time we use the more precise term automatic type narrowing because only type narrowing is safe.

function foo(arg: unknown) {
    if (typeof arg === "string") {
        // We know 'arg' is a string now.
        console.log(arg.toUpperCase()); }}Copy the code

In versions prior to Typescript 4.4, if we assign this judgment to a variable and use it in the if branch, we can’t narrow the type properly:

function foo(arg: unknown) {
    const argIsString = typeof arg === "string";
    if (argIsString) {
        console.log(arg.toUpperCase());
        / / ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
        // Error! Property 'toUpperCase' does not exist on type 'unknown'.}}Copy the code

This problem is fixed in Typescript 4.4, which actually deepens the type narrowing judgment logic so that it works wherever it is written. So type narrowing can also be inferred from the following deconstructive usage judgments:

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; }}Copy the code

Beyond simple judgments, Typescript 4.4 also supports compound type inference:

function doSomeChecks(
    inputA: string | undefined,
    inputB: string | undefined,
    shouldDoExtraWork: boolean.) {
    const mustDoWork = inputA && inputB && shouldDoExtraWork;
    if (mustDoWork) {
        // We can access 'string' properties on both 'inputA' and 'inputB'!
        const upperA = inputA.toUpperCase();
        const upperB = inputB.toUpperCase();
        // ...}}Copy the code

A branch of mustDoWork that is true means that inputA and inputB are narrowed to string.

This deep judgment is also reflected in the calculation of a variable with type judgment, and the generated variable also has type judgment function:

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'.}}Copy the code

As you can see, we can write Typescript almost like we write Js, and 4.4 supports most of the intuitive derivation very conveniently. It’s important to note that Typescript isn’t runtime and doesn’t allow for more thorough auto-inference, but it does support most scenarios.

Subscript support Symbol and template string type determination

Originally we defined an object to be accessed with subscripts like this:

interface Values {
  [key: string] :number
}
Copy the code

Symbol pull is now also supported:

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"; // Error: Type 'string' is not assignable to type 'number'.
Copy the code

It also supports type matching for certain string templates. For example, you want a subscript starting with data- to be a separate type.

interfaceOptions { 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-'.
    [optName: `data-The ${string}`]: unknown;
}

let b: OptionsWithDataProps = {
    width: 100.height: 100."data-blah": true.// Works!

    "unknown-property": true.// Error! 'unknown-property' wasn't declared in 'OptionsWithDataProps'.
};
Copy the code

This is very useful for data- attributes in HTML.

Joint type definitions are also supported. The following two types are equivalent:

interface Data {
    [optName: string | symbol]: any;
}

// Equivalent to

interface Data {
    [optName: string] :any;
    [optName: symbol]: any;
}
Copy the code

More stringent types of error catching

Prior to unknown types, Typescript used any as the default type for throwing errors. After all, no one knew what type was thrown:

try {
    // Who knows what this might throw...
    executeSomeThirdPartyCode();
}
catch (err) { // err: any
    console.error(err.message); // Allowed, because 'any'
    err.thisWillProbablyFail(); // Allowed, because 'any' :(
}
Copy the code

Who knows what this might throw… A function can have a runtime error anywhere, which is beyond static analysis, so it’s impossible to automatically infer the type of error, so use any.

In the Typescript 4.4 – useUnknownInCatchVariables or strict mode will be unknown as capture to the wrong type by default.

Unlike the nonexistent type never, unknown simply means that it is of unknown type, so it cannot be used as any type like any, but it can be inferred as any type:

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); }}Copy the code

If this is too much trouble, you can also re-declare the type as any:

try {
    executeSomeThirdPartyCode();
}
catch (err: any) {
    console.error(err.message); // Works again!
}
Copy the code

This is not a good idea, however, because it is theoretically possible for unexpected errors to occur even when runtime factors are taken into account, so it is not a good idea to be overconfident with type inference about errors. It is best to keep the type unknown and handle all possible boundary cases.

Explicit optional properties

The optional attributes of an object have ambiguities in the type description, such as:

interface Person {
    name: string, age? :number;
}
Copy the code

Typescript defines its types as:

interface Person {
    name: string, age? :number | undefined;
}
Copy the code

Why do we define it this way? Because in many cases, not having this key is equivalent to having this key as undefined. However, for example in the Object. Keys scenario, the two representations are not equivalent, so in theory for age? “Number” is either no age or has an age of type number.

// With 'exactOptionalPropertyTypes' on:
const p: Person = {
    name: "Daniel".age: undefined.// Error! undefined isn't a number
};
Copy the code

In the Typescript 4.4 at the same time open – exactOptionalPropertyTypes and strictNullChecks can take effect.

If you think about it, it makes sense to assign undefined, even if the object is of an optional type, because age? Keys (age); keys (age); keys (age); keys (age);

Support the Static Block

Typescript 4.4 supports Class static Blocks and has access to private variables within the code block scope.

There are also some performance improvements and experience improvements, but you can read the original document: Perf-Improvements.

conclusion

With Typescript 4.4, we can see that Typescript is moving toward “more native JS-friendly” features, which will undoubtedly make Typescript more usable.

For more new features, check out the Typescript 4.5 release schedule.

The discussion address is: read Typescript 4.4 · Issue #348 · dt-fe/weekly

If you’d like to participate in the discussion, pleaseClick here to, with a new theme every week, released on weekends or Mondays. Front end Intensive Reading – Helps you filter the right content.

Pay attention to the front end of intensive reading wechat public account

Copyright Notice: Freely reproduced – Non-commercial – Non-derivative – Remain signed (Creative Commons 3.0 License)