Welcome to wechat public account: Front Reading Room

High-level types

Intersection Types

Cross typing is merging multiple types into one type. This allows us to superimpose existing types into a single type that contains all the required features of the type. For example, Person & Serializable & Loggable are both Person and Serializable and Loggable. This means that an object of this type has members of all three types.

We mostly see the use of cross types in mixins or other places that don’t fit into a typical object-oriented model. (There are plenty of occasions when this happens in JavaScript!) Here’s a simple example of how to create mixins:

function extend<T.U> (first: T, second: U) :T & U {
    letresult = <T & U>{}; for (let id in first) { (<any>result)[id] = (<any>first)[id]; } for (let id in second) { if (! result.hasOwnProperty(id)) { (<any>result)[id] = (<any>second)[id]; } } return result; } class Person { constructor(public name: string) { } } interface Loggable { log(): void; } class ConsoleLogger implements Loggable { log() { // ... } } var jim = extend(new Person("Jim"), new ConsoleLogger()); var n = jim.name; jim.log();Copy the code

Union Types

Union types are closely related to cross types, but their usage is completely different. Occasionally you’ll run into a code base that wants to pass in a number or string argument. For example, the following function:

/** * Takes a string and adds "padding" to the left. * If 'padding' is a string, then 'padding' is appended to the left side. * If 'padding' is a number, then that number of spaces is added to the left side. */
function padLeft(value: string, padding: any) {
    if (typeof padding === "number") {
        return Array(padding + 1).join("") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'. `);
}

padLeft("Hello world".4); // returns " Hello world"
Copy the code

A problem with padLeft is that the padding parameter is of type any. This means that we can pass in an argument that is neither number nor string, but TypeScript does not report an error.

let indentedString = padLeft("Hello world".true); // Compile phase passed, run error
Copy the code

In a traditional object-oriented language, we might abstract these two types into hierarchical types. This is clearly clear, but there is also over-design. One of the benefits of the original version of padLeft is that it allows us to pass in primitive types. It’s easy and convenient to use. This new approach won’t work if we just want to use existing functions.

Instead of any, we can use the union type as the padding argument:

/** * Takes a string and adds "padding" to the left. * If 'padding' is a string, then 'padding' is appended to the left side. * If 'padding' is a number, then that number of spaces is added to the left side. */
function padLeft(value: string, padding: string | number) {
    // ...
}

let indentedString = padLeft("Hello world".true); // errors during compilation
Copy the code

Union types indicate that a value can be one of several types. We use a vertical bar (|) separated for each type, so the number | string | Boolean said a value can be a number, a string, or Boolean.

If a value is a union type, we can access only the members that are common to all types of that union type.

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function getSmallPet() :Fish | Bird {
    // ...
}

let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim();    // errors
Copy the code

The union types here can be a bit complicated, but you can easily get used to them. If A value is of type A | B, we can sure of is that it contains A and B some members of the communist party of China. In this example, Bird has a fly member. We are not sure a Bird if there is a fly way | Fish types of variables. If the variable is of type Fish at run time, the call to pet.fly() is an error.

Type Guards and Differentiating Types

Union types are suitable for cases where values can be of different types. But what happens when we want to know for sure if it’s Fish? A common way to distinguish between two possible values in JavaScript is to check if the member exists. As mentioned earlier, we can only access co-owned members of the union type.

let pet = getSmallPet();

// Every member accesses an error
if (pet.swim) {
    pet.swim();
}
else if (pet.fly) {
    pet.fly();
}
Copy the code

To make this code work, we use type assertions:

let pet = getSmallPet();

if ((<Fish>pet).swim) {
    (<Fish>pet).swim();
}
else {
    (<Bird>pet).fly();
}
Copy the code

User-defined type protection

Notice here that we have to use type assertions multiple times. It would be nice if once we checked the type, we could clearly know the type of PET in each subsequent branch.

TypeScript’s type protection makes this a reality. Type protection is expressions that are checked at run time to make sure the type is in a scope. To define a type protection, we simply define a function whose return value is a type predicate:

function isFish(pet: Fish | Bird) :pet is Fish {
    return(<Fish>pet).swim ! = =undefined;
}
Copy the code

In this case, pet is Fish is the type predicate. The predicate is of the form parameterName is Type. ParameterName must be a parameterName from the current function signature.

Whenever isFish is called with some variable, TypeScript reduces the variable to that specific type, as long as the type is compatible with the variable’s original type.

// The 'swim' and 'fly' calls are ok now

if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}
Copy the code

Note that TypeScript not only knows that pet is Fish in the if branch; It also knows that in the else branch, it must not be Fish, it must be Bird.

Typeof type protection

Now let’s go back and see how to write padLeft code using union types. We can use type assertions as follows:

function isNumber(x: any) :x is number {
    return typeof x === "number";
}

function isString(x: any) :x is string {
    return typeof x === "string";
}

function padLeft(value: string, padding: string | number) {
    if (isNumber(padding)) {
        return Array(padding + 1).join("") + value;
    }
    if (isString(padding)) {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'. `);
}
Copy the code

However, it is painful to have to define a function to determine whether a type is primitive. Fortunately, we don’t have to abstract typeof x === “number” as a function right now, because TypeScript recognizes it as type-protection. That means we can check the type directly in the code.

function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join("") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'. `);
}
Copy the code

These * typeof type protections * are recognized in only two forms: typeof V === “typename” and typeof V! “Typename”, “typename” must be “number”, “string”, “Boolean” or “symbol”. But TypeScript doesn’t prevent you from comparing with other strings, and the language doesn’t recognize those expressions as type-protected.

Instanceof protection

If you’ve read typeof type protection and are familiar with the instanceof operator in JavaScript, you’ve probably already guessed what this section is about.

Instanceof type protection is a way to refine types through constructors. For example, let’s take a look at the previous string padding example:

interface Padder {
    getPaddingString(): string
}

class SpaceRepeatingPadder implements Padder {
    constructor(private numSpaces: number){}getPaddingString() {
        return Array(this.numSpaces + 1).join(""); }}class StringPadder implements Padder {
    constructor(private value: string){}getPaddingString() {
        return this.value; }}function getRandomPadder() {
    return Math.random() < 0.5 ?
        new SpaceRepeatingPadder(4) :
        new StringPadder("");
}

/ / type for SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();

if (padder instanceof SpaceRepeatingPadder) {
    padder; // Type refined to 'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
    padder; // Type refinement to 'StringPadder'
}
Copy the code

The right side of instanceof is required to be a constructor, which TypeScript refines to:

  1. The type of the prototype property of this constructor, if it is not of type any
  2. Constructs the union of types returned by the signature

In that order.

A type that can be null

TypeScript has two special types, null and undefined, which have values null and undefined, respectively. We explained this briefly in the [Basic Types](./Basic types.md) section. By default, the type checker assumes that null and undefined can be assigned to any type. Null and undefined are valid values for all other types. This also means that you can’t prevent them from being assigned to other types, even if you wanted to. Null’s inventor, Tony Hoare, calls it a billion-dollar mistake.

StrictNullChecks the strictNullChecks flag solves this problem: when you declare a variable, it does not automatically contain null or undefined. You can explicitly include them using union types:

let s = "foo";
s = null; // error, 'null' cannot be assigned to 'string'
let sn: string | null = "bar";
sn = null; / / can

sn = undefined; / / the error, "undefined" cannot be assigned to the 'string | null'
Copy the code

Note that TypeScript treats null and undefined differently according to JavaScript semantics. String | null string | undefined and string | undefined | null is a different type.

Optional parameters and optional properties

Use – strictNullChecks, optional parameters will be automatically add | undefined:

function f(x: number, y? :number) {
    return x + (y || 0);
}
f(1.2);
f(1);
f(1.undefined);
f(1.null); // error, 'null' is not assignable to 'number | undefined'
Copy the code

The same is true for optional attributes:

class C {
    a: number; b? :number;
}
let c = new C();
c.a = 12;
c.a = undefined; // error, 'undefined' is not assignable to 'number'
c.b = 13;
c.b = undefined; // ok
c.b = null; // error, 'null' is not assignable to 'number | undefined'
Copy the code

Type protection and type assertion

Since null-capable types are implemented through union types, you need to use type protection to remove NULls. Fortunately this is consistent with code written in JavaScript:

function f(sn: string | null) :string {
    if (sn == null) {
        return "default";
    }
    else {
        returnsn; }}Copy the code

Here null is obviously removed, and you can also use the short-circuit operator:

function f(sn: string | null) :string {
    return sn || "default";
}
Copy the code

If the compiler is unable to remove null or undefined, you can do so manually using type assertions. Syntax is added! The suffix: identifier. Remove null and undefined from the identifier type:

function broken(name: string | null) :string {
  function postfix(epithet: string) {
    return name.charAt(0) + '. the ' + epithet; // error, 'name' is possibly null
  }
  name = name || "Bob";
  return postfix("great");
}

function fixed(name: string | null) :string {
  function postfix(epithet: string) {
    returnname! .charAt(0) + '. the ' + epithet; // ok
  }
  name = name || "Bob";
  return postfix("great");
}
Copy the code

This example uses nested functions because the compiler cannot remove null from nested functions (except for function expressions that are called immediately). Because it can’t keep track of all calls to nested functions, especially if you use the inner function as the return value of the outer function. If you don’t know where the function is called, you don’t know the type of name at the time of the call.

Type the alias

A type alias gives a type a new name. Type aliases are sometimes similar to interfaces, but can work with primitive values, union types, tuples, and any other type you need to write by hand.

type Name = string;
type NameResolver = () = > string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver) :Name {
    if (typeof n === 'string') {
        return n;
    }
    else {
        returnn(); }}Copy the code

Aliasing does not create a new type – it creates a new name to refer to that type. Aliasing primitive types is usually useless, although it can be used as a form of documentation.

Like interfaces, type aliases can also be generic – we can add type parameters and pass them to the right of the alias declaration:

type Container<T> = { value: T };
Copy the code

We can also use type aliases to refer to ourselves in attributes:

type Tree<T> = {
    value: T;
    left: Tree<T>;
    right: Tree<T>;
}
Copy the code

With cross types, we can create some pretty bizarre types.

type LinkedList<T> = T & { next: LinkedList<T> };

interface Person {
    name: string;
}

var people: LinkedList<Person>;
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;
Copy the code

However, the type alias cannot appear anywhere on the right side of the declaration.

type Yikes = Array<Yikes>; // error
Copy the code

Interfaces vs. type aliases

As we mentioned, type aliases can act like interfaces; However, there are some nuances.

First, the interface creates a new name that can be used anywhere else. Type aliases do not create new names – for example, they are not used for error messages. In the following example code, hovering over interfaced in the compiler shows that it returns Interface, but hovering over Aliased shows the object literal type.

type Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias) :Alias;
declare function interfaced(arg: Interface) :Interface;
Copy the code

Another important difference is that type aliases cannot extends and implements (nor themselves can extends and implements other types). Because objects in software should be open to extension but closed to modification, you should try to use interfaces instead of type aliases.

On the other hand, if you cannot describe a type through an interface and need to use a union or tuple type, type aliases are often used.

String literal type

The string literal type allows you to specify a fixed value that a string must have. In practice, string literal types work well with union types, type protection, and type aliases. By combining these features, you can implement strings that resemble enumerations.

type Easing = "ease-in" | "ease-out" | "ease-in-out";
class UIElement {
    animate(dx: number, dy: number, easing: Easing) {
        if (easing === "ease-in") {
            // ...
        }
        else if (easing === "ease-out") {}else if (easing === "ease-in-out") {}else {
            // error! should not pass null or undefined.}}}let button = new UIElement();
button.animate(0.0."ease-in");
button.animate(0.0."uneasy"); // error: "uneasy" is not allowed here
Copy the code

String literals can also be used to distinguish function overloading:

function createElement(tagName: "img") :HTMLImageElement;
function createElement(tagName: "input") :HTMLInputElement;
// ... more overloads ...
function createElement(tagName: string) :Element {
    // ... code goes here ...
}
Copy the code

Numeric literal type

TypeScript also has numeric literal types.

function rollDie() : 1 | 2 | 3 | 4 5 | | 6{
    // ...
}
Copy the code

We rarely use it directly.

Enumerator type

As we mentioned in the enumeration section, enumerators are typed when each enumerator is initialized with a literal.

When we talk about “singletons,” we mostly mean enumerator types and number/string literals, although most users use “singletons” and “literals” interchangeably.

Discriminated Unions

You can combine singletons, union types, type protection, and type aliases to create a high-level pattern called recognizable union, also known as label union or algebraic data types. Discernible unions are useful in functional programming. Some languages automatically recognize associations for you; TypeScript is based on existing JavaScript patterns. It has three elements:

  1. Has common singleton type properties-recognizable characteristics.
  2. A type alias contains the union – union of those types.
  3. Type protection on this property.
interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}
Copy the code

First we declare the interfaces to be federated. Each interface has a kind attribute but has a different string literal type. A kind attribute is called an identifiable feature or tag. Other attributes are specific to each interface. Note that there is currently no connection between the interfaces. Let’s put them together:

type Shape = Square | Rectangle | Circle;
Copy the code

Now we use recognizable union:

function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2; }}Copy the code

Integrity check

We want the compiler to be able to notify us when not all recognizable union changes are covered. For example, if we added Triangle to Shape, we would also need to update the area:

type Shape = Square | Rectangle | Circle | Triangle;
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
    }
    // should error here - we didn't handle case "triangle"
}
Copy the code

There are two ways to do this. The first is to enable –strictNullChecks and specify a return value type:

function area(s: Shape) :number { // error: returns number | undefined
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2; }}Copy the code

Because switch doesn’t cover all cases, TypeScript assumes that this function will sometimes return undefined. If you explicitly specify the return value type for the number, then you will see an error, because actually return a value of type number | is undefined. However, there are some subtleties to this approach and strictNullChecks does not support old code very well.

The second method uses the never type, which the compiler uses for completeness checks:

function assertNever(x: never) :never {
    throw new Error("Unexpected object: " + x);
}
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;
        default: return assertNever(s); // error here if there are missing cases}}Copy the code

Here, assertNever checks whether S is of type never — that is, the type that remains after all possible cases have been removed. If you forget a case, s will have a true type and you will get an error. This approach requires you to define an extra function, but it’s also more obvious when you forget a case.

Polymorphic this type

The polymorphic this type represents a subtype that contains a class or interface. This is called F-bounded polymorphism. It can easily represent inheritance between coherent interfaces, such as. In the calculator example, we return this after each operation:

class BasicCalculator {
    public constructor(protected value: number = 0){}public currentValue(): number {
        return this.value;
    }
    public add(operand: number) :this {
        this.value += operand;
        return this;
    }
    public multiply(operand: number) :this {
        this.value *= operand;
        return this;
    }
    // ... other operations go here ...
}

let v = new BasicCalculator(2)
            .multiply(5)
            .add(1)
            .currentValue();
Copy the code

Since this class uses the this type, you can inherit it, and the new class can use the previous method without making any changes.

class ScientificCalculator extends BasicCalculator {
    public constructor(value = 0) {
        super(value);
    }
    public sin() {
        this.value = Math.sin(this.value);
        return this;
    }
    // ... other operations go here ...
}

let v = new ScientificCalculator(2)
        .multiply(5)
        .sin()
        .add(1)
        .currentValue();
Copy the code

Without this type, ScientificCalculator would not be able to inherit from BasicCalculator while maintaining interface continuity. Multiply will return the BasicCalculator, which has no sin method. However, with this type, multiply returns this, which in this case is ScientificCalculator.

Index types

Using index types, the compiler can examine code that uses dynamic attribute names. For example, a common JavaScript pattern is to pick a subset of properties from an object.

function pluck(o, names) {
    return names.map(n= > o[n]);
}
Copy the code

Here’s how to use this function in TypeScript with index type queries and index access operators:

function pluck<T.K extends keyof T> (o: T, names: K[]) :T[K] []{
  return names.map(n= > o[n]);
}

interface Person {
    name: string;
    age: number;
}
let person: Person = {
    name: 'Jarid'.age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
Copy the code

The compiler checks to see if name is really an attribute of Person. This example also introduces several new type operators. The first is keyof T, the index type query operator. For any type T, the result of keyof T is the union of known public attribute names on T. Such as:

let personProps: keyof Person; // 'name' | 'age'
Copy the code

Keyof Person is can completely with the ‘name’ | ‘age’ replace each other. Different is that if you add other attributes to the Person, such as address: string, then keyof Person will automatically become a ‘name’ | ‘age’ | ‘address’. You can use keyof in a context like the pluck function because you don’t know the property name that might appear before you use it. But the compiler will check that you passed the correct property name to Pluck:

pluck(person, ['age'.'unknown']); // error, 'unknown' is not in 'name' | 'age'
Copy the code

The second operator is T[K], the index access operator. In this case, type syntax mirrors expression syntax. This means that person[‘name’] has type person[‘name’] – in our case, string. However, just like index-type queries, you can use T[K] in a normal context, and that’s where its power lies. You just have to make sure that the type variable K extends Keyof T. For example, the getProperty function looks like this:

function getProperty<T.K extends keyof T> (o: T, name: K) :T[K] {
    return o[name]; // o[name] is of type T[K]
}
Copy the code

GetProperty o: T and name: K mean o[name]: T[K]. When you return the result T[K], the compiler instantiates the true type of the key, so the return type of getProperty changes with the property you want.

let name: string = getProperty(person, 'name');
let age: number = getProperty(person, 'age');
let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'
Copy the code

Index type and string index signature

Keyof and T[K] interact with string index signatures. If you had a type with a string index signature, keyof T would be string. And T[string] is the type of the index signature:

interface Map<T> {
    [key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number> ['foo']; // number
Copy the code

Mapping type

A common task is to make each attribute of a known type optional:

interfacePersonPartial { name? :string; age? :number;
}
Copy the code

Or we want a read-only version:

interface PersonReadonly {
    readonly name: string;
    readonly age: number;
}
Copy the code

As is often the case in JavaScript, TypeScript provides a way to create new types from old ones – mapping types. In a mapping type, the new type converts every attribute of the old type in the same way. For example, you can make each attribute of type Readonly or optional. Here are some examples:

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};
type Partial<T> = {
  [P inkeyof T]? : T[P]; };Copy the code

Here’s a look at the simplest mapping type and its components:

type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };
Copy the code

Its syntax is the same as the syntax type of the index signature, internally using for.. The in. It has three parts:

  1. Type variable K, which is bound to each property in turn.
  2. The Keys of the string literal union, which contains the collection of property names to iterate over.
  3. The result type of the property.

In a simple example, Keys is a hard-coded list of attribute names and the attribute type is always Boolean, so the mapping type is equivalent to:

type Flags = {
    option1: boolean;
    option2: boolean;
}
Copy the code

In a real application, this might be different from Readonly or Partial above. They convert fields in a certain way, based on some existing type. This is what keyof and index access types do:

type NullablePerson = { [P in keyof Person]: Person[P] | null }
type PartialPerson = { [P inkeyof Person]? : Person[P] }Copy the code

But it would be more useful if there were some generic versions.

type Nullable<T> = { [P in keyof T]: T[P] | null }
type Partial<T> = { [P inkeyof T]? : T[P] }Copy the code

In these cases, the property list is keyof T and the result type is a variant of T[P]. This is a good template for using generic mapping types. Because this type of transformation is homomorphic, the mapping only works on the properties of T and nothing else. The compiler knows that it can copy all existing attribute modifiers before adding any new attributes. For example, if person.name is read-only, Partial . Name will also be read-only and optional.

Here is another example where T[P] is wrapped in a Proxy

class:

type Proxy<T> = {
    get(): T;
    set(value: T): void;
}
type Proxify<T> = {
    [P in keyof T]: Proxy<T[P]>;
}
function proxify<T> (o: T) :Proxify<T> {
   // ... wrap proxies ...
}
let proxyProps = proxify(props);
Copy the code

Note that Readonly

and Partial

are useful, so they’re included in the TypeScript standard library along with Pick and Record:

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}
type Record<K extends string, T> = {
    [P in K]: T;
}
Copy the code

Readonly, Partial, and Pick are homomorphic, but Record is not. Because Record does not require an input type to copy properties, it is not a homomorphism:

type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3'.string>
Copy the code

Non-homomorphic types essentially create new properties, so they do not copy property modifiers from them.

Inferred from the mapping type

Now that you know how to wrap a type’s properties, it’s time to unpack them. It’s actually pretty easy:

function unproxify<T> (t: Proxify<T>) :T {
    let result = {} as T;
    for (const k in t) {
        result[k] = t[k].get();
    }
    return result;
}

let originalProps = unproxify(proxyProps);
Copy the code

Note that this unpacking inference applies only to homomorphic mapping types. If the mapping type is not homomorphic, you need to give the unpacking function an explicit type parameter.

Predefined conditional types

TypeScript 2.8 adds predefined conditional types in lib.d.ts:

  • Exclude<T, U>— Removes types from T that can be assigned to U.
  • Extract<T, U>Extract the type from T that can be assigned to U.
  • NonNullable<T>— Remove null and undefined from T.
  • ReturnType<T>Gets the function return value type.
  • InstanceType<T>Gets the instance type of the constructor type.
type T00 = Exclude<"a" | "b" | "c" | "d"."a" | "c" | "f">;  // "b" | "d"
type T01 = Extract<"a" | "b" | "c" | "d"."a" | "c" | "f">;  // "a" | "c"

type T02 = Exclude<string | number | (() = > void), Function>;  // string | number
type T03 = Extract<string | number | (() = > void), Function>;  // () => void

type T04 = NonNullable<string | number | undefined>;  // string | number
type T05 = NonNullable<(() = > string) | string[] | null | undefined>;  // (() => string) | string[]

function f1(s: string) {
    return { a: 1.b: s };
}

class C {
    x = 0;
    y = 0;
}

type T10 = ReturnType<() = > string>;  // string
type T11 = ReturnType<(s: string) = > void>;  // void
type T12 = ReturnType<(<T>() = > T)>;  / / {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]
type T14 = ReturnType<typeof f1>;  // { a: number, b: string }
type T15 = ReturnType<any>;  // any
type T16 = ReturnType<never>;  // any
type T17 = ReturnType<string>;  // Error
type T18 = ReturnType<Function>;  // Error

type T20 = InstanceType<typeof C>;  // C
type T21 = InstanceType<any>;  // any
type T22 = InstanceType<never>;  // any
type T23 = InstanceType<string>;  // Error
type T24 = InstanceType<Function>;  // Error
Copy the code

Note: The Exclude type is an implementation of the recommended Diff type. We use the name Exclude to avoid breaking the code that already defines the Diff, and we feel that this name better expresses the semantics of the type. We did not add the

type because it could easily be represented by Pick

>.
,>
,>

Welcome to wechat public account: Front Reading Room