黑龙江开放网站备案,百度网站入口,太原网站制作推广,基于php网站开发环境一.简介TypeScript 就引入了“泛型”#xff08;generics#xff09;。泛型的特点就是带有“类型参数”#xff08;type parameter#xff09;。在日常 TypeScript 编程中#xff0c;我们经常会遇到这样的场景#xff1a;函数的参数类型与返回值类型密切相关。此时#…一.简介TypeScript 就引入了“泛型”generics。泛型的特点就是带有“类型参数”type parameter。在日常 TypeScript 编程中我们经常会遇到这样的场景函数的参数类型与返回值类型密切相关。此时泛型Generics就成为了我们编写灵活、高复用性代码的重要工具。来看一个例子来明白泛型的重要性:
function getFristValue(arr: number[]): any {return arr[0]; // 返回数组的第一个数据
}
这段代码虽然工作正常但 any 类型 丢失了类型之间的联系。我们无法知道传入的是 string[] 还是 number[]返回值类型也就不明确了。于是我们使用泛型来表达这种“输入与输出类型相关”的关系
function getFirstT(arr: T[]): T {return arr[0];
}
这里的 T 就是类型参数它类似于函数中的变量调用函数时再决定 T 的具体类型。比如
function getFirstT(arr: T[]): T {return arr[0];
}getFirstnumber([1, 2, 3]); // 返回 number 类型
getFirst([a, b, c]); // 推断为 string 类型
二.泛型的写法1.function函数function关键字定义的泛型函数类型参数放在尖括号中写在函数名后面。
function funT(a: T): T {return a;
}console.log(funnumber(1));
那么对于变量形式定义的函数泛型有下面两种写法。
let my_function: T(a: T) T function T(a: T): T {return a;
};let you_function: typr(a: typr) typr function typr(a: typr): typr {return a;
};my_functionnumber(10); // 10
you_functionstring(hello); // hello
2.interface接口
interface BoxT {contents: T;
}let box: Boxnumber { contents: 123 };
3.class类
class PairK, V {constructor(public key: K, public value: V) {}
}const kv new Pairstring, number(age, 30);
也可以设置默认值
class GenericT string {list: T[] [];add(item: T) {this.list.push(item);}
}const g new Generic();
g.add(hello); // 正确
g.add(123); // 报错
注意泛型类不能使用类型参数定义静态属性。
class ExampleT {static prop: T; // 报错
}
4.type类型别名
type NullableT T | null | undefined;type ContainerT { value: T };const a: Containernumber { value: 42 };
三.类型参数的默认值类型参数可以设置默认值。使用时如果没有给出类型参数的值就会使用默认值。
function getFirstT string(arr: T[]): T {return arr[0];
}上面示例中T string表示类型参数的默认值是string。调用getFirst()时如果不给出T的值TypeScript 就认为T等于string。若调用时未显式提供类型TypeScript 会自动推断但默认值只有在无法推断时才会生效。
一旦类型参数有默认值就表示它是可选参数。如果有多个类型参数可选参数必须在必选参数之后。
function combineT, U, V boolean(a: T, b: U, c: V): [T, U, V] {return [a, b, c];
}const res1 combine(1, hello, true); // [number, string, boolean]
const res2 combine(a, 2, false); // [string, number, boolean]
const res3 combine(x, 3, undefined); // [string, number, boolean]
四.数组的泛型表示TypeScript 原生的数据结构如数组、Map、Set、Promise 都是泛型结构《数组》一章提到过数组类型有一种表示方法是ArrayT。这就是泛型的写法Array是 TypeScript 原生的一个类型接口T是它的类型参数。声明数组时需要提供T的值。
let arr: Arraynumber [1, 2, 3];上面的示例中Arraynumber就是一个泛型类型参数的值是number表示该数组的全部成员都是数值。同样的如果数组成员都是字符串那么类型就写成Arraystring。事实上在 TypeScript 内部数组类型的另一种写法number[]、string[]只是Arraynumber、Arraystring的简写形式。在 TypeScript 内部Array是一个泛型接口类型定义基本是下面的样子。
interface ArrayType {length: number;pop(): Type | undefined;push(...items: Type[]): number;// ...
}其他的 TypeScript 内部数据结构比如Map、Set和Promise其实也是泛型接口完整的写法是MapK, V、SetT和PromiseT。TypeScript 默认还提供一个ReadonlyArrayT接口表示只读数组。
function doStuff(values: ReadonlyArraystring) {values.push(hello!); // 报错
}上面示例中参数values的类型是ReadonlyArraystring表示不能修改这个数组所以函数体内部新增数组成员就会报错。因此如果不希望函数内部改动参数数组就可以将该参数数组声明为ReadonlyArrayT类型。五.类型参数的约束条件TypeScript 提供了一种语法允许在类型参数上面写明约束条件如果不满足条件编译时就会报错。这样也可以有良好的语义对类型参数进行说明。
function compT extends { length: number }(a: T, b: T) {if (a.length b.length) {return a;}return b;
}上面示例中T extends { length: number }就是约束条件表示类型参数 T 必须满足{ length: number }否则就会报错。
comp([1, 2], [1, 2, 3]); // 正确
comp(ab, abc); // 正确
comp(1, 2); // 报错上面示例中只要传入的参数类型不满足约束条件就会报错。类型参数的约束条件采用下面的形式。
TypeParameter extends ConstraintType上面语法中TypeParameter表示类型参数extends是关键字这是必须的ConstraintType表示类型参数要满足的条件即类型参数应该是ConstraintType的子类型。类型参数可以同时设置约束条件和默认值前提是默认值必须满足约束条件。
常见的约束类型约束类型示例含义说明{ length: number }T extends { length: number }要求有 length 属性string、number 等原始类型T extends string只能传入对应类型自定义接口或类T extends Person要求 T 是 Person 类型或其子类联合类型T extends string | numberT 只能是 string 或 number
六.注意点1.尽量少用泛型。泛型虽然灵活但是会加大代码的复杂性使其变得难读难写。一般来说只要使用了泛型类型声明通常都不太易读容易写得很复杂。因此可以不用泛型就不要用。2.类型参数越少越好。多一个类型参数多一道替换步骤加大复杂性。因此类型参数越少越好3.类型参数需要出现两次。如果类型参数在定义后只出现一次那么很可能是不必要的。4.泛型可以嵌套。类型参数可以是另一个泛型。