In front-end development, not only do we need to create consistent, well-defined apis, but we also need to consider reusability. The ability of components to support not only current data types but also future data types gives you a lot of flexibility when building large systems. This is where ts generics play a key role.
Generics: Basic usage
(1): Routine use
Now there is a requirement to encapsulate a method, take a parameter (type number), and return a result of the same type.
const indentify1 = (args: number): number= > {
return args;
}
Copy the code
What if the type received is number or string?
const indentify2 = (args: number | string): number | string= > {
return args;
}
Copy the code
What if there are more and more parameter types? Generics work.
const indentify3 = <T>(args: T): T= > {
return args; // T is like a variable that guarantees the same type of input and output parameters.
}
Copy the code
Generics act as constraints. Generics can provide more than one.
const indentify4 =
(args: T, mes: U): T => { return args; // Provide a T and U generic type that declares the input parameter type and specifies the return result type. }
,>Copy the code
(2): Generic interface
As with indentify4, what happens to generics if the function returns a collection of types?
Method one: Return a collection of types
const indentify5 =
(args: T, mes: U): [T,U] => { return [args, mes]; // Too many constraints, outer constraints on generics}
,>Copy the code
Method two: Generic interfaces
You can provide an interface to make the parameters of the interface generic. For example 🌰 : define a person function that accepts name, age, gender, and marriage. The last return.
interface IPersonView<T, U, V> {
name: T,
age: U,
sex: T,
isMarray: V
}
const per = <T, U, V>(name: T, age: U, sex: T, isMarray: V):IPersonView<T, U, V> => {
let result: IPersonView<T, U, V> = {
name, age, sex, isMarray
}
return result;
}
Copy the code
As you can see, using generic interfaces is a friendly way to solve these problems. Easy to maintain and unify.
Two: common operators
(1): extends
It simply means to inherit, to have a type variable inherit from the type we’ve defined. Play a big role in generic constraints. (More on generic constraints later)
(2): keyof
This operator can be used to get all keys of a type whose return type is the union type. An 🌰 :
interface Person {
name: string;
age: number;
isMarray: boolean;
}
type per1 = keyof Person // string | number | boolean
Copy the code
Three: generic constraints
Sometimes we may want to limit the number of types accepted per type variable, which is where generic constraints come in. Acts as a constraint on type variables.
For example 🌰 : When working with strings or arrays, we assume that the length attribute is available. When we use a function and try to print the length of the argument, some problems arise.
const indentify5 = <T>(args: T):T= > {
console.log(args.length); Attribute 'length' not found on // // type 'T'
return args;
}
Copy the code
The reason for this problem is that because the TS compiler does not know, it cannot recognize whether or not a property is present on T. We can define a type and make the type variable extends.
interface ILength {
length: number // Define an interface that contains length
}
const indentify6 = <T extends ILength>(args: T):T => {
console.log(args.length); // number
return args;
}
Copy the code
Of course: we can also define array types to solve this problem.
const indentify7 = <T>(args: T[]): T[] => {
console.log(args.length);
return args;
}
const indentify8 = <T>(args: Array<T>): Array<T> => {
console.log(args.length);
return args;
}
Copy the code
We can also use keyof to determine whether a key exists on an object. An 🌰 :
Declare a type interface. interface Ip {name: string,
sex: string,
age: number
}
// With the keyof operator, we can get all the keys of the specified type, and then we can combine the extends constraint
// That is, the attribute name of the restricted input is contained in the union type returned by keyof
const indentify9 =
(obj: Ip , key: K): Ip[k] { return obj[key]; }
,>Copy the code
Generic tools
(1): Partial
Make all attributes of a type non-required.
type Partial<T> = {
[P inkeyof T]? : T[P];/ /! To become?
};
Copy the code
An 🌰 :
interface IPerson {
name: string,
age: number } Partial<IPerson> === interface IPerson { name? : string |undefined.age: number | undefined
}
Copy the code
(2): Exclude
Remove certain types of T that belong to U
type Exclude<T, U> = T extends U ? never : T;
Copy the code
type T = Exclude<"a" | "b" | "c"."a"> // "b" | "c"
Copy the code
(3): ReturnType
ReturnType is used to get the ReturnType of function T.
type T1 = ReturnType<() = > String> // string
type T2 = ReturnType<(args: String) = > void> // void
type T3 = ReturnType<<T>() = > T>; / / {}
Copy the code
The above is some simple use, welcome to point out.