Write a function where the type of input is related to the type of output, or the types of two inputs are related in some way. Let us consider a function that returns the first element of an array:
function firstElement(arr: any[]) {
return arr[0];
}
This function does its job, but unfortunately the return type is any. It would be better if the function returns the type of the array elements.
In TypeScript, when we want to describe the correspondence between two values, we use generics. We do this by declaring a type parameter in the function signature:
function firstElement<T>(arr: T[]): T {
return arr[0];
}
const arr: string[] = ['1', '2', '3'];
const result = firstElement(arr);
console.log(result);
const result1:number = firstElement(arr);
Type inference of TypeScript-Type inference
function map<Input, Output>(arr: Input[], func: (arg: Input) => Output): Output[] {
return arr.map(func);
}
Note that Inout and Output in the above code can be replaced by T and V, but the readability is not as good as the former.
Type constrains-Type constraints
We have written some general functions that can handle any type of value. Sometimes we want to associate two values, but we can only operate on a subset of a value. In this case, we can use constraints to limit the types of types that the type parameter can accept.
Let's write a function that returns the longer of the two values. For this, we need a length attribute, which is a number. We restrict the type parameter to this type by writing the extends clause:
function longest<Type extends { length: number }>(a: Type, b: Type) {
if (a.length >= b.length) {
return a;
} else {
return b;
}
}
// longerArray is of type 'number[]'
const longerArray = longest([1, 2], [1, 2, 3]);
console.log(longerArray);
// longerString is of type 'string'
const longerString = longest("alice", "bob");
console.log(longerString);
// Error! Numbers don't have a 'length' property
const notOK = longest(10, 100);
The last function call will have a compilation error, because the TypeScript basic type number does not have an attribute named length.
Compared with Java, the power of TypeScript's type constraints is that the built-in type of TypeScript does not need to be followed by extends, such as in this example:
{
length: number
}
This means that as long as the type passed by the function contains at least the length attribute of type number.
Specifying Type Arguments
TypeScript can usually infer the expected type parameters in generic calls, but this is not always the case. For example, suppose you wrote a function to combine two arrays:
function combine<Type>(arr1: Type[], arr2: Type[]): Type[] {
return arr1.concat(arr2);
}
Compile Error:
Solution: Use angle bracket syntax to explicitly pass in the type parameter: here T = string | number, which means that either string or number type can be received.
Best practices for writing generic functions
Writing generic functions is fun, and it's easy to get confused by type parameters. Having too many type parameters or using constraints where they are not needed can make reasoning less successful and frustrate the caller of the function.
Best Practice 1-Push Type Parameters Down
function firstElement1<Type>(arr: Type[]) {
return arr[0];
}
function firstElement2<Type extends any[]>(arr: Type) {
return arr[0];
}
// a: number (good)
const a = firstElement1([1, 2, 3]);
// b: any (bad)
const b = firstElement2([1, 2, 3]);
At first glance, the two methods seem to be the same, but firstElement1 is a better way to write this function. Its inferred return type is Type, but the inferred return type of firstElement2 is any, because TypeScript must resolve the arr[0] expression using the constraint type instead of "waiting" to resolve the element during the call.
Best Practice 2- Use Fewer Type Parameters
function filter1<Type>(arr: Type[], func: (arg: Type) => boolean): Type[] {
return arr.filter(func);
}
function filter2<Type, Func extends (arg: Type) => boolean>(
arr: Type[],
func: Func
): Type[] {
return arr.filter(func);
}
We created a type parameter Func that does not associate two values. This is always a red flag because it means that callers who want to specify a type parameter must manually specify an additional type parameter for no reason. Func will not do anything, it will only make the function harder to read and reason about!
Best Practice 3-Type Parameters Should Appear Twice
function greet<Str extends string>(s: Str) {
console.log("Hello, " + s);
}
greet("world");
In this example, Str appears only once, so it is not necessary at all.
Can be abbreviated as:
function greet(s: string) {
console.log("Hello, " + s);
}
Remember, type parameters are used to associate multiple value types. If a type parameter is used only once in the function signature, it does not matter.
More original articles by Jerry, all in: "Wang Zixi":
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。