This article introduces the usage scenarios of advanced TypeScript types in detail, which can provide some help for the daily use of TypeScript.
preface
This article has been included in Github: github.com/beichensky/…[1] In the middle, pass by and point a Star
1, Advanced type
& cross type
Cross type is to combine multiple types into one type. This allows us to stack existing types together into one type, which contains all the required types of features.
-
Syntax: T & U
Its return type must comply with T The type shall also comply with U type
-
Usage: suppose there are two interfaces: one is Ant Ant interface, one is Fly Flying interface, now there is a flying ant:
-
interface Ant { name: string; weight: number; } interface Fly { flyHeight: number; speed: number; } // If any attribute is missing, an error will be reported const flyAnt: Ant & Fly = { name: 'Ants hey', weight: 0.2, flyHeight: 20, speed: 1, };
Union type (|)
Union types are very related to cross types, but they are completely different in use.
-
Syntax: T | U
Its return type is any one of the multiple types of the connection
-
Usage: suppose to declare a data, which can be string Type, or number type
-
let stringOrNumber: string | number = 0 stringOrNumber = ''
Look at the following example, start The argument type of the function is Bird | Fish, then start Function, if you want to call directly, you can only call Bird and Fish All have methods, otherwise the compilation will report an error
class Bird { fly() { console.log('Bird flying'); } layEggs() { console.log('Bird layEggs'); } } class Fish { swim() { console.log('Fish swimming'); } layEggs() { console.log('Fish layEggs'); } } const bird = new Bird(); const fish = new Fish(); function start(pet: Bird | Fish) { // call layEggs No problem, because Bird perhaps Fish Both layEggs method pet.layEggs(); // Property 'fly' does not exist on type 'Bird | Fish' // pet.fly(); // An error will be reported: Property 'swim' does not exist on type 'Bird | Fish' // pet.swim(); } start(bird); start(fish);
2, Keywords
Type constraints (extensions)
Syntax: T extends K
The extensions here are not the inheritance of classes and interfaces, but the judgment and constraints on types, which means to judge whether T can be assigned to K
You can constrain incoming types in generics
const copy = (value: string | number): string | number => value // Can only be passed in string perhaps number copy(10) // An error will be reported: Argument of type 'boolean' is not assignable to parameter of type 'string | number' // copy(false)
You can also judge whether t can be assigned to U. if possible, return T; otherwise, return never
type Exclude<T, U> = T extends U ? T : never;
Type mapping (in)
It will traverse the key of the specified interface or the union type
interface Person { name: string age: number gender: number } // take T All properties of are converted to read-only types type ReadOnlyType<T> = { readonly [P in keyof T]: T } // type ReadOnlyPerson = { // readonly name: Person; // readonly age: Person; // readonly gender: Person; // } type ReadOnlyPerson = ReadOnlyType<Person>
Type predicate (is)
-
Syntax: parameterName is Type
parameterName It must be a parameter name from the current function signature to judge whether parametername is Type.
Specific application scenarios can follow the following code ideas:
After reading the example of union type, you may consider: if you want to start Function, call according to the situation Bird of fly Methods and Fish of swim Method, how to operate it?
The first thought may be to directly check whether the member exists, and then call:
function start(pet: Bird | Fish) { // call layEggs No problem, because Bird perhaps Fish Both layEggs method pet.layEggs(); if ((pet as Bird).fly) { (pet as Bird).fly(); } else if ((pet as Fish).swim) { (pet as Fish).swim(); } }
However, it is troublesome to perform type conversion during judgment and call. You may want to write a tool function to judge:
function isBird(bird: Bird | Fish): boolean { return !!(bird as Bird).fly; } function isFish(fish: Bird | Fish): boolean { return !!(fish as Fish).swim; } function start(pet: Bird | Fish) { // call layEggs No problem, because Bird perhaps Fish Both layEggs method pet.layEggs(); if (isBird(pet)) { (pet as Bird).fly(); } else if (isFish(pet)) { (pet as Fish).swim(); } }
It seems a little concise, but when calling a method, we still need to carry out type conversion, otherwise we will still report an error. What good way is there to enable us to call the method directly after judging the type without type conversion?
OK, there must be, type predicate is It comes in handy
-
Usage:
function isBird(bird: Bird | Fish): bird is Bird { return !!(bird as Bird).fly } function start(pet: Bird | Fish) { // call layEggs No problem, because Bird perhaps Fish Both layEggs method pet.layEggs(); if (isBird(pet)) { pet.fly(); } else { pet.swim(); } };
Whenever you use some variables to call isFish TypeScript The variable is reduced to that specific type, as long as the type is compatible with the original type of the variable.
TypeScript not only knows that pet is a Fish type in the if branch; It is also clear that in the else branch, it must not be Fish type, but Bird type
Type to be inferred (infer)
Can use infer P To mark a generic type, indicating that the generic type is a type to be inferred and can be used directly
For example, the following example of obtaining function parameter types:
type ParamType<T> = T extends (param: infer P) => any ? P : T; type FunctionType = (value: number) => boolean type Param = ParamType<FunctionType>; // type Param = number type OtherParam = ParamType<symbol>; // type Param = symbol
Judge whether T can be assigned to (param: infer P) = > any, and infer the parameter as generic P. if it can be assigned, return the parameter type p; otherwise, return the passed in type
Another example of obtaining the return type of a function:
type ReturnValueType<T> = T extends (param: any) => infer U ? U : T; type FunctionType = (value: number) => boolean type Return = ReturnValueType<FunctionType>; // type Return = boolean type OtherReturn = ReturnValueType<number>; // type OtherReturn = number
Judge whether T can be assigned to (param: any) = > infer U, and infer the return value type as generic U. if it can be assigned, return the return value type P, otherwise return the incoming type
Original type protection (typeof)
-
Syntax: typeof v === "typename" or typeof v !== "typename"
It is used to judge whether the data type is an original type (number, string, boolean, symbol) and protect the type
'typename 'must be' number ',' string ',' boolean ', or' symbol '. But TypeScript does not prevent you from comparing with other strings, and the language does not recognize those expressions as type protected.
Look at the following example, print The function will print different results according to the parameter type. How to judge whether the parameter is string still number And?
function print(value: number | string) { // If it is string type // console.log(value.split('').join(', ')) // If it is number type // console.log(value.toFixed(2)) }
There are two common judgment methods:
-
Depending on whether it contains split Attribute judgment yes string Type, including toFixed Method judgment is number type
Disadvantages: type conversion is required for both judgment and call
-
Using type predicates is
Disadvantages: it's too troublesome to write a tool function every time
-
Usage: Here we are typeof It's time to show your skills
function print(value: number | string) { if (typeof value === 'string') { console.log(value.split('').join(', ')) } else { console.log(value.toFixed(2)) } }
After using typeof for type determination, TypeScript will reduce the variable to the specific type, as long as the type is compatible with the original type of the variable.
Type protection (instanceof)
And typeof Similar, but in different ways, instanceof Type protection is a way to refine types through constructors.
instanceof The right side of the requires a constructor, TypeScript Refine to:
-
Of this constructor prototype Property, if its type is not any Words
-
Construct the union of the types returned by the signature
Or with Type predicate is Demonstrate the code in the example:
Initial code:
function start(pet: Bird | Fish) { // call layEggs No problem, because Bird perhaps Fish Both layEggs method pet.layEggs(); if ((pet as Bird).fly) { (pet as Bird).fly(); } else if ((pet as Fish).swim) { (pet as Fish).swim(); } }
use instanceof Code after:
function start(pet: Bird | Fish) { // call layEggs No problem, because Bird perhaps Fish Both layEggs method pet.layEggs(); if (pet instanceof Bird) { pet.fly(); } else { pet.swim(); } }
The same effect can be achieved
Index type query operator (keyof)
-
Syntax: keyof T
For any type T, keyof T The result is T Known on Public attribute name of union
interface Person { name: string; age: number; } type PersonProps = keyof Person; // 'name' | 'age'
Here, keyof Person The returned type is the same as the union type of 'name' | 'age', and can be completely replaced with each other
-
Usage: keyof Only known on type can be returned Public attribute name
class Animal { type: string; weight: number; private speed: number; } type AnimalProps = keyof Animal; // "type" | "weight"
For example, we often get a property value of an object, but we are not sure which property it is. We can use it at this time extends coordination typeof Limit the property name. The parameter passed in can only be the property name of the object
const person = { name: 'Jack', age: 20 } function getPersonValue<T extends keyof typeof person>(fieldName: keyof typeof person) { return person[fieldName] } const nameValue = getPersonValue('name') const ageValue = getPersonValue('age') // An error will be reported: Argument of type '"gender"' is not assignable to parameter of type '"name" | "age"' // getPersonValue('gender')
Index access operator (T[K])
-
Syntax: T[K]
be similar to js Using object indexes in, but js Is to return the value of the object property, and in ts Is returned in T Type of corresponding attribute P
-
Usage:
interface Person { name: string age: number weight: number | string gender: 'man' | 'women' } type NameType = Person['name'] // string type WeightType = Person['weight'] // string | number type GenderType = Person['gender'] // "man" | "women"
3, Mapping type
Read only type (readonly < T >)
-
definition:
type Readonly<T> = { readonly [P in keyof T]: T[P]; }
Used to T All properties of type are set to read-only.
-
Usage:
interface Person { name: string age: number } const person: Readonly<Person> = { name: 'Lucy', age: 22 } // Can't assign to 'name' because it is a read only property person.name = 'Lily'
readonly Read only, by readonly Marked properties can only be assigned in the declaration or in the constructor of the class, and then they will not be modifiable (i.e. read-only properties)
Read only array (readonlyarray < T >)
-
definition:
interface ReadonlyArray<T> { /** Iterator of values in the array. */ [Symbol.iterator](): IterableIterator<T>; /** * Returns an iterable of key, value pairs for every entry in the array */ entries(): IterableIterator<[number, T]>; /** * Returns an iterable of keys in the array */ keys(): IterableIterator<number>; /** * Returns an iterable of values in the array */ values(): IterableIterator<T>; }
Variables can only be assigned when the array is initialized, and the array cannot be modified after that
-
use:
interface Person { name: string } const personList: ReadonlyArray<Person> = [{ name: 'Jack' }, { name: 'Rose' }] // An error will be reported: Property 'push' does not exist on type 'readonly Person []' // personList.push({ name: 'Lucy' }) // However, if the internal element is a reference type, the element itself can be modified personList[0].name = 'Lily'
Optional type (partial < T >)
Used to T All properties of type are set to optional status, first through keyof T, fetch type T All properties of, and then through in Operator, and finally add ?, Make the attribute optional.
-
definition:
type Partial<T> = { [P in keyof T]?: T[P]; }
-
Usage:
interface Person { name: string age: number } // Error will be reported: Type '{}' is missing the following properties from type 'Person': name, age // let person: Person = {} // use Partial New type returned after mapping, name and age All become optional properties let person: Partial<Person> = {} person = { name: 'pengzu', age: 800 } person = { name: 'z' } person = { age: 18 }
Required type (required < T >)
and Partial The effect is opposite
Used to T All properties of type are set to the required status. First, through keyof T, fetch type T All properties of, and then through in Operator, and finally after the attribute ? Add before -, Make the attribute mandatory.
-
definition:
type Required<T> = { [P in keyof T]-?: T[P]; }
-
use:
interface Person { name?: string age?: number } // use Required New type returned after mapping, name and age All become required attributes // Error will be reported: type '{}' is missing the following properties from type 'required < person >': name, age let person: Required<Person> = {}
Extract attributes (pick < T >)
-
definition:
type Pick<T, K extends keyof T> = { [P in K]: T[P]; }
from T Type as a new return type.
-
Usage: for example, when sending a network request, we only need to pass some attributes in the type Pick To achieve.
interface Goods { type: string goodsName: string price: number } // As a network request parameter, only goodsName and price Can type RequestGoodsParams = Pick<Goods, 'goodsName' | 'price'> // Return type: // type RequestGoodsParams = { // goodsName: string; // price: number; // } const params: RequestGoodsParams = { goodsName: '', price: 10 }
Exclude attributes (omit < T >)
-
Definition: type omit < T, K extends keyof T > = pick < T, exclude < keyof T, k > >
and Pick On the contrary, it is used to T Type, excluding some properties
-
Usage: for example, a cuboid has length, width and height, while a cube has the same length, width and height, so it only needs to be long, so it can be used at this time Omit The type of next generation cube
-
interface Rectangular { length: number height: number width: number } type Square = Omit<Rectangular, 'height' | 'width'> // Return type: // type Square = { // length: number; // } const temp: Square = { length: 5 }
Extraction type (extract < T, u >)
-
Syntax: extract < T, u >
extract T Medium can assignment to U Type of
-
Definition: type extract < T, u > = t extends u? T : never;
-
Usage:
-
type T01 = Extract<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "a" | "c" type T02 = Extract<string | number | (() => void), Function>; // () => void
Exclusion type (exclude < T, u >)
-
Syntax: exclude < T, u >
And Extract Contrary to the usage, from T Can be assigned to Type of U
-
Definition: type exclude < T, u > = t extends u? never : T
-
Usage:
-
type T00 = Exclude<"a" | "b" | "c" | "d", "a" | "c" | "f">; // "b" | "d" type T01 = Exclude<string | number | (() => void), Function>; // string | number
Attribute mapping (record < K, t >)
-
definition:
type Record<K extends string | number | symbol, T> = { [P in K]: T; }
Receive two generics, K Must be assignable to string | number | symbol Type of, by in Operator pair K For traversal, the type of each attribute must be T type
-
Usage: for example, we want to Person Type array into object mapping, you can use Record To specify the type of mapping object
interface Person { name: string age: number } const personList = [ { name: 'Jack', age: 26 }, { name: 'Lucy', age: 22 }, { name: 'Rose', age: 18 }, ] const personMap: Record<string, Person> = {} personList.map((person) => { personMap[person.name] = person })
For example, when passing a parameter, if you want the parameter to be an object, but you don't know the specific type, you can use it Record As parameter type
function doSomething(obj: Record<string, any>) { }
Non nullable type (nonnullable < T >)
-
Definition: type nonnullable < T > = t extends null | undefined? never : T
Remove from T null,undefined,never Type, not culled void,unknow type
type T01 = NonNullable<string | number | undefined>; // string | number type T02 = NonNullable<(() => string) | string[] | null | undefined>; // (() => string) | string[] type T03 = NonNullable<{name?: string, age: number} | string[] | null | undefined>; // {name?: string, age: number} | string[]
Constructor parameter type (constructorparameters < typeof T >)
Returns a class consisting of constructor parameter types Tuple type
-
definition:
/** * Obtain the parameters of a constructor function type in a tuple */ type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
-
use:
class Person { name: string age: number weight: number gender: 'man' | 'women' constructor(name: string, age: number, gender: 'man' | 'women') { this.name = name this.age = age; this.gender = gender } } type ConstructorType = ConstructorParameters<typeof Person> // [name: string, age: number, gender: "man" | "women"] const params: ConstructorType = ['Jack', 20, 'man']
Instance type (instancetype < T >)
Gets the return type of the class constructor
-
definition:
/** * Obtain the return type of a constructor function type */ type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
-
use:
class Person { name: string age: number weight: number gender: 'man' | 'women' constructor(name: string, age: number, gender: 'man' | 'women') { this.name = name this.age = age; this.gender = gender } } type Instance = InstanceType<typeof Person> // Person const params: Instance = { name: 'Jack', age: 20, weight: 120, gender: 'man' }
Function parameter type (parameters < T >)
Gets the parameter type of the function tuple
-
definition:
/** * 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;
-
Usage:
type FunctionType = (name: string, age: number) => boolean type FunctionParamsType = Parameters<FunctionType> // [name: string, age: number] const params: FunctionParamsType = ['Jack', 20]
Function return value type (ReturnType < T >)
Gets the return value type of the function
-
definition:
/** * Obtain the return type of a function type */ type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
-
use:
type FunctionType = (name: string, age: number) => boolean | string type FunctionReturnType = ReturnType<FunctionType> // boolean | string
4, Summary
-
Advanced type
usage describe & Cross type, merging multiple types into one type, intersection \ Union type, which combines multiple types into one type, can be any one of multiple types, and can be combined -
keyword
usage describe T extends U Type constraint to determine whether T can be assigned to U P in T Type mapping, traversing all types of T parameterName is Type Type predicate to judge whether the function parameter parameterName is type infer P The type to be inferred can be used by marking type P with infer typeof v === "typename" Primitive type protection determines whether the data type is an primitive type (number, string, boolean, symbol) instanceof v Type protection, which determines whether the type of data is a constructor prototype Attribute type keyof Index type query operator, which returns the information known on the type Public attribute name T[K] Index access operator, return T Type of corresponding attribute P -
mapping type
usage describe Readonly Make all attributes in T read-only ReadonlyArray Returns a read-only array of type T ReadonlyMap<T, U> Returns a read-only Map composed of T and U types Partial Make all attributes in T optional types Required Make all attributes in T mandatory Pick<T, K extends keyof T> Extract some attributes from T Omit<T, K extends keyof T> Exclude some attributes from T Exclude<T, U> Eliminate the types that can be assigned to U from T Extract<T, U> Extract the types in T that can be assigned to U Record<K, T> Returns a type with a property name of K and a property value of T NonNullable Eliminate null and undefined from T ConstructorParameters Gets a tuple of T's constructor parameter types InstanceType Gets the instance type of T Parameters Gets a tuple of function parameter types ReturnType Gets the return value type of the function