- 泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
- 泛型T作用域只限于函数内部使用
泛型函数
首先,我们来实现一个函数 createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值
使用泛型 ```typescript function createArrayfunction createArray(length: number, value: any):Array<any> {const arr = [];for (let i = 0; i < length; i++) {arr[i] = value;}return arr;}console.log(createArray(5, 2)); // [ 2, 2, 2, 2, 2 ]
(length: number, value: T): Array { const arr = []; for (let i = 0; i < length; i++) { arr[i] = value; } return arr; }
console.log(createArray
<a name="tQSFr"></a># 类数组类数组(Array-like Object)不是数组类型,比如 arguments```typescriptlet root = document.getElementById('root');let children: HTMLCollection = (root as HTMLElement ).children;children.length;let nodeList: NodeList = (root as HTMLElement).childNodes;nodeList.length;
泛型类
class MyArray<T> {private arr: T[] = []add(val: T) {this.arr.push(val);}}const arr1 = new MyArray<number>();arr1.add(1)// Argument of type 'string' is not assignable to parameter of type 'number'.// arr.add('')const arr2 = new MyArray<string>();arr2.add('hello')
泛型和 new
class Person { }class Animal {}function factory<T>(type: { new(): T }): T{return new type();}factory(Person);factory(Animal);
泛型接口
泛型接口可以用来约束函数
interface Sum {<T>(a: T, b: T): T;}const sum: Sum = function <T>(a: T, b: T): T {// 报错:Operator '+' cannot be applied to types 'T' and 'T'. 这是因为 a,b 可能是任意类型,不一定能使用加号// return a + b;return a;}// Argument of type 'string' is not assignable to parameter of type 'number'.// sum<number>(1, '');sum<number>(1, 2);
定义接口的时候也可以指定泛型
interface Sum<T> {(a: T, b: T): T;}const sum: Sum<number> = function(a: number, b: number): number {return a;};
interface Sum<T> {<U>(a:T, b:T):U}const sum: Sum<number> = function <U>(a: number, b: number): U { return a as any };sum<number>(1, 2);
多个类型参数
泛型可以有多个
function swap<A, B>(tuple: [A, B]): [B, A] {return [tuple[1], tuple[0]];}console.log(swap([1, 'a'])); // ['a', 1]
interface Sum<T> {<U>(a:T, b:T):U}const sum: Sum<number> = function <U>(a: number, b: number): U { return a as any };sum<number>(1, 2);
默认泛型类型
function createArr<T = number>(length: number, val: T): Array<T> {const arr = [];for (let i = 0; i < length; i++) {arr[i] = val;}return arr;}createArr(1, 2)createArr<string>(1, 'a')
泛型约束
在函数中使用泛型的时候,由于预先并不知道泛型的类型,所以不能随意访问相应类型的属性或方法。
function logge1<T>(val: T) {// Property 'length' does not exist on type 'T'.// console.log(val.length);}interface Length {// length 属性length: number;}function logger2<T extends Length>(val: T) {console.log(val.length);}logger2<string>('a');// Type 'number' does not satisfy the constraint 'Length'.// logger2<number>(1);const obj = { name: 'f', age: 18, length: 1};type O = typeof obj;logger2<O>(obj);
class GrandFather {grandFather = '';}class Father extends GrandFather {father = '';}class Child extends Father {child = '';}let grandFather = new GrandFather();let father = new Father();let child = new Child();// Property 'child' is missing in type 'Father' but required in type 'Child'.// child = father;// 判断兼容和不兼容跟 extends 继承没有一点关系,只看形状有没有对应father = child;function get<T extends Father>() { }get<Child>()get<Father>()// Type 'GrandFather' does not satisfy the constraint 'Father'.Property 'father' is missing in type 'GrandFather' but required in type 'Father'.// get<GrandFather>()
compose
type Func<T extends any[], R> = (...a: T) => R;console.log(compose()('f')); // 'f'function fn1(str:string):string { return str + 'i'; }console.log(compose(fn1)('f')); // 'fi'function fn2(str: string): string { return str + 'e'; }console.log(compose(fn1, fn2)('f')) // 'fei'export default function compose(): <R>(a: R) => R;export default function compose<F extends Function>(f: F): F;/* two functions */export default function compose<A, T extends any[], R>(f1: (a: A) => R, f2: Func<T, A>): Func<T, R>;/* three functions */export default function compose<A, B, T extends any[], R>(f1: (b: B) => R, f2: (a: A) => B, f3: Func<T, A>): Func<T, R>;/* four functions */export default function compose<A, B, C, T extends any[], R>(f1: (c: C) => R,f2: (b: B) => C,f3: (a: A) => B,f4: Func<T, A>): Func<T, R>;/* rest */export default function compose<R>(f1: (a: any) => R, ...funcs: Function[]): (...args: any[]) => R;export default function compose<R>(...funcs: Function[]): (...args: any[]) => R;export default function compose(...funcs: Function[]) {if (funcs.length === 0) {return <T>(arg: T) => arg;}if (funcs.length === 1) {return funcs[0];}return funcs.reduce((a, b) => (...args: any) => a(b(...args)));}
function add<T extends string | number>(x: T, y: T): T {return x + y;}add<string>("a", "b");add<number>(5, 3);add("a", "b");add(5, 3);
上述代码会报错:Operator '+' cannot be applied to types 'T' and 'T'. 我们可以改成下面这样:
function addString<T extends string>(x: T, y: T): string {return x + y;}function addNum<T extends number>(x: T, y: T): number {let a = x * y;let b = x - y;let z = x++;return x + y;}addString("a", "b");addNum(1, 2);
泛型类型别名
泛型类型别名可以表达更复杂的类型
type Arr<T> = { arr: T[]} | T[];const arr1: Arr<number> = [1, 2];const arr2: Arr<string> = { arr: [] };
泛型接口 vs 泛型类型
- 接口创建了一个新的名字,它可以在其他任意地方被调用。而类型别名并不创建新的名字,例如报错信息就不会使用别名
- 类型别名不能被 extends和 implements,这时我们应该尽量使用接口代替类型别名
- 当我们需要使用联合类型或者元组类型的时候,类型别名会更合适
- 能使用 interface 实现的不用 type
