- 泛型(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
```typescript
let 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