TS: Generic
1 Introduction
-
Sometimes, if we want to make the parameters and return types of a function the same, we can use type variables .
-
A type variable is a special type of variable used to represent a type rather than a value.
function identity<T>(arg : T) : T{ return arg; }
-
After the generic function is defined, it can be used in two ways.
-
One is to pass in all parameters, including type parameters:
let out = identity<string>("yivi");
-
The other is to use type inference-that is, the compiler will automatically infer the type:
let out = identity("yivi");
-
Type inference helps us keep the code simple and highly readable.
2. Generic variables
-
Let's take a look at an example:
function foo<T>(arg : T): T{ console.log(arg.length); // error,arg的类型为T,无明确指示方法,因此报错。 return arg; }
-
When we want to manipulate an array of type T,
.length
the property exists, so no error will be reported:function foo<T>(args : T[]):T[]{ console.log(args.length); return args; } // or function foo<T>(args : Array<T>) : Array<T>{ console.log(args.length); return args; }
3. Generic types
-
We can use different generic parameter names, as long as the number and usage are consistent;
-
You can also use object literals with call signatures to define generic functions;
function identity<T>(arg : T) : T{ return arg; } let myIdentity : <U>(arg : U) => U = identity; // or let myIdentity : { <T>(arg : T):T} = identity;
-
Take the object literal above and write it as an interface:
interface IdentityInterface{ <T>(arg : T) : T; } function identity<T> (arg : T) : T{ return arg; } let myidentity : IdentityInterface = identity;
-
It is also possible to treat a generic parameter as a parameter of the entire interface, so as to ensure that we know which generic type is being used:
interface IdentityInterface<T>{ (arg : T) : T; } function identity<T> (arg : T) : T{ return arg; } let myidentity : IdentityInterface<string> = identity;
-
Generic classes are similar to generic interfaces, and both use <> for constraints:
class Foo<T>{ state : T; add : (x : T, y : T) => T; } let myfoo = new Foo<number>(); myfoo.state = 1; myfoo.add = (x,y)=>{ return x + y; }
4. Generic constraints
-
In the above example, we mentioned
.length
that if this attribute is used on generics, an error will be reported; -
Therefore, we only need to implement an interface and let the generic type have this attribute to prevent the compiler from reporting errors;
interface Length{ length : number; } function identity<T extends Length>(arg : T) : T{ console.log(arg.length); return arg; }
-
Although the compiler will not report an error, if you use a type without this attribute, it will also report an error;
-
So we need to pass in a value that meets the constraints and contains the necessary attributes;
identity({ length : 10,value : 100});
5. Use class types in generics
-
When using generics to create factory functions in TypeScript, you need to refer to the class type of the constructor.
function create<T> (c : { new() : T}) : T{ return new c(); } class Foo{ name : string; } let a : Foo = create(Foo); a.name = 'yivi' console.log(a.name)