“This is the 10th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
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 of 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
Type of 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:
- Of this constructor
prototype
Property if its type is notany
if - Constructs the union of types returned by the signature
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 can’t know where the function is called, you can’t know the type of name when it’s called, right
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
Interface VS type alias
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
You can only pass one of the three allowed characters as an argument, passing any other value will generate an error
Argument of type '"uneasy"' is not assignable to parameter of type '"ease-in" | "ease-out" | "ease-in-out"'
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 them directly, but they can be used to narrow down the scope of bug debugging:
function foo(x: number) {
if(x ! = =1|| x ! = =2) {
/ / ~ ~ ~ ~ ~ ~ ~
// Operator '! ==' cannot be applied to types '1' and '2'.}}Copy the code
In other words, when x is compared to 2, its value must be 1, which means that the comparison check above is illegal
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:
- With ordinary singleton type attributes —Recognizable features
- A type alias contains the union of those types —The joint
- 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
Polymorphism of thethis
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
Use it like this:
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
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:
- Type variable
K
, which is bound to each property in turn - String literal associative
Keys
, which contains a collection of property names to iterate over - 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
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>
– fromT
The cull can be assigned toU
The type of.Extract<T, U>
Extract –T
Can be assigned toU
The type of.NonNullable<T>
– fromT
In rejectingnull
andundefined
.ReturnType<T>
Gets the function return value type.InstanceType<T>
Gets the instance type of the constructor type.
The sample
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
,>
>