Utility Types in TypeScript
TS has many built-in Utility Types globally, which can greatly improve our development efficiency. So this article is a detailed introduction, understanding, grasp.
Partial<Type>
What it does: It makes all properties of Type optional and returns a subset of the given Type.
Example:
interface Todo {
title: string;
description: string;
}
// Scenario: you only want to update Partial toTo properties. Partial is elegant
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return{... todo, ... fieldsToUpdate }; }const todo1 = {
title: "organize desk".description: "clear clutter"};const todo2 = updateTodo(todo1, {
description: "throw out trash"});Copy the code
Let’s look behind Partial:
/** * Make all properties in T optional */
type Partial<T> = {
[P inkeyof T]? : T[P]; };Copy the code
Knowledge points in the above definition:
- The generic
<T>
keyof
Operator: gets all keys of T[P in keyof T]
: iterates over all keys of T,Mapping type, index signature?
: optional
Required<Type>
Required: As opposed to Partial above, the build returns a new Type for which all properties of a Type are Required.
Example:
interfaceProps { a? :number; b? :string;
}
const obj: Props = { a: 5 };
const obj2: Required<Props> = { a: 5 }; // Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.
Copy the code
Let’s look at the implementation behind Required:
/** * Make all properties in T required */
type Required<T> = {
[P inkeyof T]-? : T[P]; };Copy the code
Knowledge points in the above definition:
Improved support for mapping type modifiers in TS2.8.
Before TS2.8, it was possible to add readonly,? , but does not provide the ability to remove the modifier. By default, its modifiers are consistent with the mapping type. For those interested, see the PR and the issue it fixes. So now the mapping type it supports adding or removing readonly or? The modifier.
Let’s look at an example:
type A = { readonly a? : number.b: string };
type MockRequired<T> = {
-readonly [P inkeyof T]-? : T[P]// Do not need -?
};
const test: MockRequired<A> = { // I hope a is necessary
a: 10.b: 'b'
};
test.a = 20; // I hope that a can be modified
Copy the code
Here we understand -? The meaning of.
Readonly<Type>
Effect: Set all properties of Type to read-only. Example:
interface Todo {
title: string;
}
const todo: Readonly<Todo> = {
title: "Delete inactive users"}; todo.title ="Hello"; // Cannot assign to 'title' because it is a read-only property.
Copy the code
Let’s look at the implementation behind Readonly:
/** * Make all properties in T readonly */
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Copy the code
To make it easier to understand, just know that the mapping type supports the modifiers readonly,? .
Readonly: cannot rewrite (overwrite assignment);
This method works well for the definition of object.freeze:
function freeze<Type> (obj: Type) :Readonly<Type>;
Copy the code
Record<Keys,Type>
Construct an object Type whose key is from Keys and whose key corresponds to a value of Type. So this method is very useful for mapping attributes of one type to another type.
Example:
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10.breed: "Persian" },
boris: { age: 5.breed: "Maine Coon" },
mordred: { age: 16.breed: "British Shorthair"}}; cats.boris;// (property) boris: CatInfo
Copy the code
Let’s look at the definition behind Record.
/** * Construct a type with a set of properties K of type T */
type Record<K extends keyof any, T> = {
[P in K]: T;
};
Copy the code
Keyof any.
Let’s start with a piece of code:
type A = keyof any;
type EqualA = string | number | symbol; // A is equivalent to EqualA
type Is = A extends EqualA ? true : false;
const is: Is = false; // Type 'false' is not assignable to type 'true'.
Copy the code
So if we use it like this we get an error:
interface CatInfo {
age: number;
breed: string;
}
type CatName = "miffy" | "boris" | "mordred" | false; / / false cause
const cats: Record<CatName, CatInfo> = { // Error: Type 'string | boolean' does not satisfy the constraint 'string | number | symbol'. Type 'boolean' is not assignable to type 'string | number | symbol'.
miffy: { age: 10.breed: "Persian" },
boris: { age: 5.breed: "Maine Coon" },
mordred: { age: 16.breed: "British Shorthair"}};Copy the code
Pick<Type, Keys>
Keys are required: String literal or union of string literals.
What it does: Build a new Type that returns the attributes that are selected from the Type Type according to Keys.
Example code:
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
const todo: TodoPreview = { Keys: title and completed
title: "Clean room".completed: false}; todo;Copy the code
Again, let’s look at the implementation behind it: nothing new here.
/** * From T, pick a set of properties whose keys are in the union K */
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Copy the code
Omit<Type, Keys>
Rather than repeat it here, see my previous article on TypeScript Learning more Omit.
Exclude<Type, ExcludedUnion>
Effect: Excludes types from Type that can be assigned to ExcludedUnion.
Example:
type T0 = Exclude<"a" | "b" | "c"."a">; // type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c"."a" | "b">; // type T1 = "c"
type T2 = Exclude<string | number | (() = > void), Function>; // type T2 = string | number
Copy the code
Let’s look at the implementation behind Exclude:
/** * Exclude from T those types that are assignable to U */
type Exclude<T, U> = T extends U ? never : T;
Copy the code
Related knowledge:
T extends U ? Never: T Extends here is not the same thing as the extends of class. This is a conditional type. Here do not do too much extension, focus on the concept of distributed condition type to understand the above expression of Exclude.
type A = 'a' | 'b' | 'c';
type B = 'a';
type C = Exclude<A, B>; // 'b' | 'c';
// A extends B ? Never: A equivalent to (' A '|' b '|' c ') extends b? Never: (' a '|' b '|' c ') is equivalent to the following
type D = ('a' extends B ? never : 'a') | ('b' extends B ? never : 'b') | ('c' extends B ? never : 'c'); // 'b' | 'c';
Copy the code
Extract<Type, Union>
Action: Checks out the types that can be assigned to the Union from Type. Example:
type T0 = Extract<"a" | "b" | "c"."a" | "f">; // type T0 = "a"
type T1 = Extract<string | number | (() = > void), Function>; // type T1 = () => void
Copy the code
Let’s look at the definition behind Extract:
/** * Extract from T those types that are assignable to U */
type Extract<T, U> = T extends U ? T : never;
Copy the code
So you can see Extract is the opposite of Exclude.
NonNullable<Type>
Use: exclude null and undefined from Type.
Example:
type T0 = NonNullable<string | number | undefined>; // type T0 = string | number
type T1 = NonNullable<string[] | null | undefined>;// type T1 = string[]
Copy the code
Look at the definition of NonNullable:
/** * Exclude null and undefined from T */
type NonNullable<T> = T extends null | undefined ? never : T;
Copy the code
We can see that this is actually an application of the distributed conditional type extends from above.
Parameters<Type>
Function: Builds a new tuple Type based on the arguments of Type Type. Example:
declare function f1(arg: { a: number; b: string }) :void;
type T0 = Parameters<() = > string>; // type T0 = []
type T1 = Parameters<(s: string) = > void>; // type T1 = [s: string]
type T2 = Parameters<<T>(arg: T) = > T>; // type T2 = [arg: unknown]
type T3 = Parameters<typeof f1>;
// type T3 = [arg: {
// a: number;
// b: string;
// }]
type T4 = Parameters<any>; // type T4 = unknown[]
type T5 = Parameters<never>; // type T5 = never
type T6 = Parameters<string>; // Type 'string' does not satisfy the constraint '(... args: any) => any'. type T6 = never
type T7 = Parameters<Function>;
// Type 'Function' does not satisfy the constraint '(... args: any) => any'.
// Type 'Function' provides no match for the signature '(... args: any): any'.
// type T7 = never
Copy the code
Let’s look at the implementation behind Parameters.
/** * Obtain the parameters of a function type in a tuple */
type Parameters<T extends(... args:any) = >any> = T extends(... args: infer P) =>any ? P : never;
Copy the code
Related knowledge:
T extends (… Args: any) => Any defines a generic constraint on Parameters and is compatible with all current function type definitions. Infer P: indicates the function parameters to be inferred.
T extends (… args: infer P) => any ? P: never: indicates that if T can be assigned to (… Args: infer P) => any Args: infer P) => The parameter in any is P. Otherwise, never is returned.
Learn more about info recommend an in-depth understanding of typescript-info.
ConstructorParameters<Type>
Action: Constructs tuples or array types from the argument types of constructor Type Type (or never if Type is not a function). Example:
type T0 = ConstructorParameters<ErrorConstructor>; // type T0 = [message?: string]
type T1 = ConstructorParameters<FunctionConstructor>; // type T1 = string[]
type T2 = ConstructorParameters<RegExpConstructor>; // type T2 = [pattern: string | RegExp, flags?: string]
type T3 = ConstructorParameters<any>; // type T3 = unknown[]
Copy the code
Take a look at its ConstructorParameters definition:
/** * Obtain the parameters of a constructor function type in a tuple */
type ConstructorParameters<T extends abstract new(... args:any) = >any> = T extends abstract new(... args: infer P) =>any ? P : never;
Copy the code
The definition of ConstructorParameters is almost the same as that of Parameters, except that the former is the definition expressing the constructor signature.
Common constructor Type signatures are based on Type or Interface.
type SomeConstructor = {
new (s: string): SomeObject;
};
function fn(ctor: SomeConstructor) {
return new ctor("hello");
}
interface CallOrConstruct {
new (s: string) :Date; (n? :number) :number;
}
Copy the code
ReturnType<Type>
Creates a new Type based on the return value Type of function Type.
Example:
declare function f1() :{ a: number; b: string };
type T0 = ReturnType<() = > string>; // type T0 = string
type T4 = ReturnType<typeof f1>;
// type T4 = {
// a: number;
// b: string;
// }
Copy the code
Source code definition:
/** * Obtain the return type of a function type */
type ReturnType<T extends(... args:any) = >any> = T extends(... args:any) => infer R ? R : any;
Copy the code
We can see that its principle is similar to the previous ones, but infer uses different positions.
InstanceType<Type>
Action: Construct a new Type based on the constructor Type of the function Type Type. Example:
class C {
x = 0;
y = 0;
}
type T0 = InstanceType<typeof C>; // type T0 = C
type T1 = InstanceType<any>; // type T1 = any
Copy the code
Source code definition:
/** * Obtain the return type of a constructor function type */
type InstanceType<T extends abstract new(... args:any) = >any> = T extends abstract new(... args:any) => infer R ? R : any;
Copy the code
The difference between InstanceType and ReturnType is that it has a function constructor signature definition. The difference between InstanceType and ConstructorParameters is that it does not infer the parameter type, but the return value type.
ThisParameterType<Type>
Gets the this Type of function Type Type. Return unknown if no.
function toHex(this: Number) {
return this.toString(16);
}
function numberToString(n: ThisParameterType<typeof toHex>) { // n: number
return toHex.apply(n);
}
Copy the code
Source code definition:
/** * Extracts the type of the 'this' parameter of a function type, or 'unknown' if the function type has no 'this' parameter. */
type ThisParameterType<T> = T extends (this: infer U, ... args:any[]) = >any ? U : unknown;
Copy the code
If you want to know how to define this in a function, you should look at the official website.
OmitThisParameter<Type>
Remove this from a function of Type Type.
Example:
function toHex(this: Number) {
return this.toString(16);
}
const fiveToHex: OmitThisParameter<typeof toHex> = toHex.bind(5); // const fiveToHex: () => string
console.log(fiveToHex());
Copy the code
Source code definition:
/** * Removes the 'this' parameter from a function type. */
type OmitThisParameter<T> = unknown extends ThisParameterType<T> ? T : T extends(... args: infer A) => infer R ?(. args: A) = > R : T;
Copy the code
Unknown extends ThisParameterType
: Returns T if this is not included in the parameter of the T function. Otherwise, T extends (… args: infer A) => infer R ? (… args: A) => R : T; If T is A subtype of the latter, then return the new function with infer A as the argument and infer R as the return value. Otherwise return T.
ending
🌹 🌹.
-
Refer to the official website.
-
The original WeChat