泛型
- 泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性
- 泛型T作用域只限于函数内部使用
1. 泛型函数
- 首先,我们来实现一个函数 createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值
ts
function createArray(length: number, value: any): Array<any> {
let result: any = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
let result = createArray(3, "x");
console.log(result);使用了泛型
ts
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
let result = createArray2<string>(3, "x");
console.log(result);2. 类数组
- 类数组(Array-like Object)不是数组类型,比如 arguments
ts
function sum() {
let args: IArguments = arguments;
for (let i = 0; i < args.length; i++) {
console.log(args[i]);
}
}
sum(1, 2, 3);
let root = document.getElementById("root");
let children: HTMLCollection = (root as HTMLElement).children;
children.length;
let nodeList: NodeList = (root as HTMLElement).childNodes;
nodeList.length;3. 泛型类
3.1 泛型类
ts
class MyArray<T> {
private list: T[] = [];
add(value: T) {
this.list.push(value);
}
getMax(): T {
let result = this.list[0];
for (let i = 0; i < this.list.length; i++) {
if (this.list[i] > result) {
result = this.list[i];
}
}
return result;
}
}
let arr = new MyArray();
arr.add(1);
arr.add(2);
arr.add(3);
let ret = arr.getMax();
console.log(ret);3.2 泛型与 new
ts
function factory<T>(type: { new (): T }): T {
return new type(); // This expression is not constructable.
}5. 泛型接口
- 泛型接口可以用来约束函数
ts
interface Calculate {
<T>(a: T, b: T): T;
}
let add: Calculate = function <T>(a: T, b: T) {
return a;
};
add<number>(1, 2);6. 多个类型参数
- 泛型可以有多个
ts
function swap<A, B>(tuple: [A, B]): [B, A] {
return [tuple[1], tuple[0]];
}
let swapped = swap<string, number>(["a", 1]);
console.log(swapped);
console.log(swapped[0].toFixed(2));
console.log(swapped[1].length);7. 默认泛型类型
ts
function createArray3<T = number>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
let result2 = createArray3(3, "x");
console.log(result2);8. 泛型约束
- 在函数中使用泛型的时候,由于预先并不知道泛型的类型,所以不能随意访问相应类型的属性或方法。
ts
function logger<T>(val: T) {
console.log(val.length); //直接访问会报错
}
//可以让泛型继承一个接口
interface LengthWise {
length: number;
}
//可以让泛型继承一个接口
function logger2<T extends LengthWise>(val: T) {
console.log(val.length);
}
logger2("hello ");
logger2(1);9. 泛型接口
- 定义接口的时候也可以指定泛型
ts
interface Cart<T> {
list: T[];
}
let cart: Cart<{ name: string; price: number }> = {
list: [{ name: "hello ", price: 10 }],
};
console.log(cart.list[0].name, cart.list[0].price);10. compose
compose
ts
type Func<T extends any[], R> = (...a: T) => R;
/**
* Composes single-argument functions from right to left. The rightmost
* function can take multiple arguments as it provides the signature for the
* resulting composite function.
*
* @param funcs The functions to compose.
* @returns A function obtained by composing the argument functions from right
* to left. For example, `compose(f, g, h)` is identical to doing
* `(...args) => f(g(h(...args)))`.
*/
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) {
// infer the argument type so it is usable in inference down the line
return <T>(arg: T) => arg;
}
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce(
(a, b) =>
(...args: any) =>
a(b(...args)),
);
}ts
import compose from ".";
/* zero functions */
console.log(compose()<string>("hello "));
/* one functions */
interface F {
(a: string): string;
}
let f: F = (a: string): string => a + "f";
console.log(compose<F>(f)("hello "));
/* two functions */
type A = string;
type R = string;
type T = string[];
let f1 = (a: A): R => a + "f1";
let f2 = (...a: T): A => a + "f2";
console.log(compose<A, T, R>(f1, f2)("hello "));11. 泛型类型别名
- 泛型类型别名可以表达更复杂的类型
ts
type Cart<T> = { list: T[] } | T[];
let c1: Cart<string> = { list: ["1"] };
let c2: Cart<number> = [1];12. 泛型接口 vs 泛型类型别名
- 接口创建了一个新的名字,它可以在其他任意地方被调用。而类型别名并不创建新的名字,例如报错信息就不会使用别名
- 类型别名不能被 extends和 implements,这时我们应该尽量使用接口代替类型别名
- 当我们需要使用联合类型或者元组类型的时候,类型别名会更合适