泛型简介
本课程介绍泛型类型。
简介
泛型通过将一个类型参数化为另一个类型,实现了更好的可重用性。一个流行的前端框架 React 可以拥有无限数量的组件,每个组件都有其独有的属性。如果要为所有可能的情况继承基类或修改框架,将会非常繁琐。因此,React 组件的属性使用了泛型。
1// 具有可根据实现变化的属性的泛型组件 2interface MyComponent<TProps> { 3 name: string; 4 id: number; 5 props: TProps; 6} 7 8// 第一个属性,包含字符串类型 9interface Props1 { 10 color: string; 11} 12 13// 第二个属性,包含数字类型 14interface Props2 { 15 size: number; 16} 17 18// 第一个组件,因为泛型参数为 Props1,所以其属性包含 color 19const component1: MyComponent<Props1> = { 20 name: "我的第一个组件", 21 id: 1, 22 props: { color: "red" } 23}; 24 25// 第二个组件,因为泛型参数为 Props2,所以其属性包含 size 26const component2: MyComponent<Props2> = { 27 name: "我的第二个组件", 28 id: 2, 29 props: { size: 100 } 30}; 31 32console.log(component1); 33console.log(component2);
第 19 行和第 26 行是泛型组件指定 props 属性将接收何种类型契约的地方。这种类型具有灵活性,可以通过在 <> 中填入任意类型来适应未来的组件需求。
泛型与列表
一个经典示例是列表。借助泛型,你可以定义一个特定类型的列表变量,比如数字列表。你可以开发一个能够处理任何类型的列表类,而通过泛型,使用者可以在不改变列表核心逻辑的前提下,自行决定列表中存储的数据类型。
在 JavaScript 中,或者在 TypeScript 引入泛型概念之前,你也可以使用松散类型 any 来实现类似功能:
1let list1: number[] = [1, 2, 3]; 2list1.push(4); // 只能添加数字 3console.log(list1); 4 5let list2: any[] = [1, 2, 3]; 6list2.push("这是一个字符串"); 7console.log(list2); // 可以添加任意类型的值
使用 any 的缺点在于,无法对传入类的类型进行约束,也无法确保其具备最小的结构定义;同时,在从类中提取数据后,还需要手动进行类型转换。
在上述示例中,第 2 行和第 6 行分别向两个不同的数组中添加数据。第一个使用的是泛型版本 number[],这等同于 Array<number>,而第二个使用的是 any[]。区别在于,第一个列表利用泛型明确限制了只能操作 number 类型的数据。
泛型与 any 的对比
以下是使用泛型替代 any 在数组中的应用示例。代码无需在类型之间进行转换,如使用 any 时所必需的那样,且数组中的所有成员都可以直接作为字符串访问。这是因为该数组被声明为 string 类型(第 1 行),因此在第 3 行可以直接调用属于字符串的方法 substr。
1const a: Array<string> = new Array("abc", "def"); 2const s: string = a[0]; // 无需类型转换 3console.log(s.substr(0,1)); // 可直接访问字符串方法
如果改用 any,则必须进行类型转换才能让 TypeScript 检测到错误。例如,第 3 行中的 substringg 多了一个字母 g,这是一个拼写错误,但使用 any 时 TypeScript 无法提供保护。
1const a2: Array<any> = new Array("abc", "def"); 2const s2 = a2[0]; // 无需类型转换 3console.log(s2.substringg(0, 1)); // TypeScript 无法进行安全防护