A very useful feature of TS is type aliases.

A type alias gives a new name to a type. Type aliases are sometimes similar to interfaces, but can be used for primitive values, union types, tuples, and anything else you need to write by hand.

Some keywords

Many complex types can be implemented using type aliases. Many complex type aliases require keywords. Let’s take a look at a few commonly used keywords:

extends

Extends extends can be used to extend a class. It can also be used to extend an interface, but it can also be used to determine conditional types:

T extends U ? X : Y;
Copy the code

The type above means that the type is X if T can be assigned to U, and Y otherwise.

The idea is to make T’ and U’ instances of T and U, respectively, and replace all type arguments with any, resolving conditional types to X if T’ can be assigned to U’, and Y otherwise.

The official explanation above is a bit convoluted, but here’s an example:

type Words = 'a'|'b'|"c";

type W<T> = T extends Words ? true : false;

type WA = W<'a'>; // -> true
type WD = W<'d'>; // -> false
Copy the code

A can be assigned to Words, so WA is true, while d cannot be assigned to Words, so WD is false.

typeof

In JS, typeof can determine the underlying data typeof a variable. In TS, it also has the function of obtaining the declaration typeof a variable or, if it does not exist, the inference typeof the variable.

Here are two examples:

interface Person {
  name: string;
  age: number; location? :string;
}

const jack: Person = { name: 'jack', age: 100 };
type Jack = typeof jack; // -> Person

function foo(x: number) :Array<number> {
  return [x];
}

type F = typeof foo; // -> (x: number) => number[]
Copy the code

The type alias Jack is actually Jack’s type Person, and the type F is TS’s own derivation of the type foo (x: number) => number[].

keyof

Keyof can be used to retrieve all the key values of an object interface:

interface Person {
    name: string;
    age: number; location? :string;
}

type K1 = keyof Person; // "name" | "age" | "location"
type K2 = keyof Person[];  // "length" | "push" | "pop" | "concat" | ...
type K3 = keyof { [x: string]: Person };  // string | number
Copy the code

in

In can iterate over enumeration types:

type Keys = "a" | "b"
type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any }
Copy the code

Above, in iterates through Keys and gives each value the type any.

infer

In conditional-type statements, you can declare a type variable with infer and use it.

We can use it to get the return type of the function. The source code is as follows:

type ReturnType<T> = T extends (
  ...args: any[]
) => infer R
  ? R
  : any;
Copy the code

In fact, infer R means to declare a variable to carry the type of the return value passed into the function signature. In simple words, infer can be used to obtain the type of the return value of the function for later use.

Built-in type aliases

Let’s take a look at some of TS’s built-in type aliases:

Partial

So Partial makes all the properties of a certain type optional, right? .

Source:

// node_modules/typescript/lib/lib.es5.d.ts

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

We can see that keyof T gets all the attribute names of T, and then in traverses, assigns the value to P, and finally T[P] gets the value of the corresponding attribute. Student: Combine the middle? To make all properties optional.

Required

Required is the opposite of Partial. Partial makes all properties optional, and Required makes all types mandatory. Here’s the source code:

// node_modules/typescript/lib/lib.es5.d.ts

type Required<T> = {
    [P inkeyof T]-? : T[P]; };Copy the code

Among them -? Does it stand for remove? The identifier of this modifier.

And then there’s a plus, right? This meaning is naturally related to -? Previously, instead, it was used to make properties optional, + omitted, see Partial.

And to extend that, in addition to applying to? This modifiers also applies to readOnly, such as readOnly.

Readonly

This type is used to make the passed property read-only.

// node_modules/typescript/lib/lib.es5.d.ts

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

Add the readonly identifier to the child attribute. If you change the readonly identifier to -readonly, you remove the readonly identifier of the child attribute.

Pick

This type can take a subtype of a type and turn it into a subtype that contains some of the properties of that type.

Source code implementation is as follows:

// node_modules/typescript/lib/lib.es5.d.ts

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

K must be the key of T, then use in to iterate, assign the value to P, and finally T[P] get the value of the corresponding attribute.

Record

This type converts the value of all attributes in K to type T. The source code implementation is as follows:

// node_modules/typescript/lib/lib.es5.d.ts

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

We can set the type of key and value based on all possible values in K. For example:

type T11 = Record<'a' | 'b' | 'c', Person>; // -> { a: Person; b: Person; c: Person; }
Copy the code

Exclude

Exclude removes a type that belongs to another type.

Source code implementation:

// node_modules/typescript/lib/lib.es5.d.ts

type Exclude<T, U> = T extends U ? never : T;
Copy the code

If T can be assigned to U, then never will be returned, otherwise T will be returned, and the end result will be to remove some of the U types from T. For example:

type T00 = Exclude<'a' | 'b' | 'c' | 'd'.'a' | 'c' | 'f'>;  // -> 'b' | 'd'
Copy the code

Can see T is’ a ‘|’ b ‘|’ c ‘|’ d ‘, and then U is’ a ‘|’ c ‘|’ f ‘, returns the type of the new type can be U to remove, namely ‘b’ | ‘d’.

Extract

The function of Extract is to Extract the elements that T contains in U, or, more semantically, to Extract U from T. The source is as follows:

// node_modules/typescript/lib/lib.es5.d.ts

type Extract<T, U> = T extends U ? T : never;
Copy the code

If T is assigned to U, then T is returned. Otherwise, never is returned. The final result is to extract attributes that are common to T and U.

type T01 = Extract<'a' | 'b' | 'c' | 'd'.'a' | 'c' | 'f'>;  // -> 'a' | 'c'
Copy the code

Can see T is’ a ‘|’ b ‘|’ c ‘|’ d ‘, and then U is’ a ‘|’ c ‘|’ f ‘, a new type of return can be T and U are extracted from some properties of the communist party of China, is the ‘a’ | ‘c’.

ReturnType

This type gets the return type of a function.

Implementation of source code

// node_modules/typescript/lib/lib.es5.d.ts

type ReturnType<T extends(... args:any[]) = >any> = T extends(... args:any[]) => infer R ? R : any;
Copy the code

In practice, you can get the ReturnType of the function by ReturnType, as shown in the following example:

function foo(x: number) :Array<number> {
  return [x];
}

type fn = ReturnType<typeof foo>; // -> number[]
Copy the code

ThisType

This type is used to specify the context object type.

// node_modules/typescript/lib/lib.es5.d.ts

interface ThisType<T> { }
Copy the code

You can see that the declaration has only one interface and no implementation, indicating that this type is supported at the TS source level, not through a type transformation.

How does this work? Here’s an example:

interface Person {
    name: string;
    age: number;
}

const obj: ThisType<Person> = {
  dosth() {
    this.name // string}}Copy the code

In this case, you can specify that the context object in all methods in obj is of type Person.

InstanceType

This type is used to get the instance type of the constructor type.

Source code implementation:

// node_modules/typescript/lib/lib.es5.d.ts

type InstanceType<T extends new(... args:any[]) = >any> = T extends new(... args:any[]) => infer R ? R : any;
Copy the code

Take a look at the official example:

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

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

NonNullable

This type can be used to filter null and undefined types.

Source code implementation:

// node_modules/typescript/lib/lib.es5.d.ts

type NonNullable<T> = T extends null | undefined ? never : T;
Copy the code

Such as:

type T22 = string | number | null;
type T23 = NonNullable<T22>; // -> string | number;
Copy the code

Parameters

This type can get a tuple of the function’s argument types.

Source code implementation:

// node_modules/typescript/lib/lib.es5.d.ts

type Parameters<T extends(... args:any[]) = >any> = T extends(... args: infer P) =>any ? P : never;
Copy the code

Here’s an example:

function foo(x: number) :Array<number> {
  return [x];
}

type P = Parameters<typeof foo>; // -> [number]
Copy the code

In this case, the true type of P is the tuple type [number] of the parameters of foo.

ConstructorParameters

Gettype () gettype () gettype () gettype () gettype ();

// node_modules/typescript/lib/lib.es5.d.ts

type ConstructorParameters<T extends new(... args:any[]) = >any> = T extends new(... args: infer P) =>any ? P : never;
Copy the code

Here’s an example:

class Person {
  private firstName: string;
  private lastName: string;
  
  constructor(firstName: string, lastName: string) {
      this.firstName = firstName;
      this.lastName = lastName; }}type P = ConstructorParameters<typeof Person>; // -> [string, string]
Copy the code

In this case, P is a tuple type [string] composed of the types of constructor’s arguments firstName and lastName in Person.

Custom type aliases

Here are some type aliases that may be used frequently, but TS does not have built-in aliases:

Omit

In such cases, you need to overwrite an attribute in the new Omit interface. In such cases, Pick and Exclude can be used to Omit certain attributes in the new Omit interface.

type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;

/ / use
type Foo = Omit<{name: string, age: number}, 'name'> // -> { age: number }
Copy the code

Mutable

Remove readonly for all properties of T:

type Mutable<T> = {
  -readonly [P in keyof T]: T[P]
}
Copy the code

PowerPartial

One limitation of built-in Partial is that it only supports handling properties of the first layer. It has no effect if multiple layers are nested. However, you can customize Partial as follows:

type PowerPartial<T> = {
    // If it is object, then the type is recursive
    [U inkeyof T]? : T[U]extends object
      ? PowerPartial<T[U]>
      : T[U]
};
Copy the code

Deferred

Same attribute name, but make the value a Promise instead of a concrete value:

type Deferred<T> = {
    [P in keyof T]: Promise<T[P]>;
};
Copy the code

Proxify

Add a proxy for the properties of T

type Proxify<T> = {
    [P in keyof T]: { get(): T[P]; set(v: T[P]): void}};Copy the code

If in doubt, please make corrections!

reference

TypeScript Chinese website

Description of the built-in types in TS

Some of TypeScript’s tools you may not know about are the use and implementation of generics