TypeScript中泛型的使用详细讲解
郑建007 人气:0一、泛型程序设计是一种编程风格或编程范式
二、案例:传入的参数类型与返回的类型一样
function identify<T>(arg: T): T {// 当前的T没有任何约束 它可以是任何类型 return arg; } const foo = identify('foo'); // foo的类型是'foo' const bar = identify('true'); // bar的类型是true
三、形式类型参数
1、形式类型参数T的默认类型为boolean类型
<T = boolean>
2、必选类型参数、可选类型参数
(1)、必选类型参数:形式类型参数没有给默认类型 , 例如: <T>
(2)、可选类型参数:形式类型参数给默认类型 , 例如: <T = boolean>
(3)、形式类型参数列表中,必选类型参数不允许出现在可选类型参数之后
<T = boolean, U> // 错误
<T, U = boolean> // 正确
四、泛型约束
在形式类型参数上允许定义一个约束条件,它能够限定类型参数的实际类型的最大范围。我们将类型参数的约束条件称为泛型约束
interface point { x: number, y: string } function getPoint<T extends point>(args: T): T { return args } console.log(getPoint({x: 123, y: 456})); // 错误 console.log(getPoint({x: 123, y: '456'})); // 正确 console.log(getPoint({x: 123, y: '456', z: 678})); // 正确 //参数的前俩个必须有并且类型必须正确 否则错误
可以同时定义泛型约束和默认类型
<T extends number = 0 | 1>
泛型约束 ==> 类型参数
<T, U extends T> <T extends U, U>
形式类型参数不允许直接或间接地将其自身作为约束类型
<T extends T> // 错误 <T extends U, U extends T> // 错误
类型参数T没有声明泛型约束,那么类型参数T的基约束为空对象类型字面量“{}”。除了undefined类型和null类型外,其他任何类型都可以赋值给空对象类型字面量
<T> // 类型参数T的基数约为“{}”类型
五、泛型函数
1.简介:若一个函数的函数签名(形参)中带有类型参数,那么它是一个泛型函数
2.f1函数两个参数的类型相同,函数返回值类型是数组,数组元素类型 == 参数类型。
function f1<T>(x: T, y: T): T[] { return [x, y] } const a = f1(123, 456) const b = f1('123', '456')
3.f2函数两个参数的类型不同,返回值类型为对象类型。返回值对象类型中x属性的类型与参数x类型相同,y属性的类型与参数y类型相同
function f2<T, U>(x: T, y: U): { x: T, y: U} { return { x, y } } const a = f2('123', 456) const b = f2(123, '456')
4.f3函数接受两个参数,参数a为任意类型的数组;参数f是一个函数,该函数的参数类型与参数a的类型相同,并返回任意类型。f3函数的返回值类型为参数f返回值类型的数组。
(这个代码的粘贴的 我也懵逼)
function f3<T, U>(a: T[], f: (x: T) => U): U[] { return a.map(f); } // f3<number, boolean> 约束T为number类型 U为boolean类型 const a: boolean[] = f3<number, boolean>([1, 2, 3], n => !! n)
六、泛型函数类型推断
function f0<T>(x: T): T { return x } const a = f0(123) // 推断出类型为 123 const b = f0('123') // 推断出类型为 '123'
此例中编译器推断出的不是number 和 string类型,而是字符串字面量类型123和“123”。因为TS原则,始终将字面量视为字面量类型,只在必要的时候才会将字面量类型放宽为某种基础类型,例如string类型。此例中,字符串字面量类型“123”是比string类型更加精确的类型。
类型参数只在函数签名中出现一次,则说明它与其他值没有关联,则不需要使用类型参数,直接声明实际类型即可。
几乎任何函数都可以声明为泛型函数。若泛型函数的类型参数不表示参数之间或参数与返回值之间的某种关系,那么使用泛型函数可能是一种反模式。
// 没必要使用泛型 function f<T>(x: T): void { console.log(x) } // 直接限定就好了 function f(x: number): void { console.log(x) }
补充:应用场景
通过上面初步的了解,后述在编写 typescript 的时候,定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性的时候,这种情况下就可以使用泛型
灵活的使用泛型定义类型,是掌握typescript 必经之路
<泛型变量名称>(参数1: 泛型变量, 参数2: 泛型变量, ...参数n: 泛型变量) => 泛型变量
/*------------基础使用方法------------*/ function join<T, P>(first: T, second: P): T { return first; } //const twoParms = join<number, string>(1, '我是string'); const twoParms = join(1, '我是string'); /*---------泛型集合--------------*/ function map<T>(params: Array<T>) { return params; } //const sanleType = map<string>(['123']); const sanleType = map(['123']); /* -----------泛型箭头函数-------------*/ const identity = <T,>(arg: T): T => { return arg; }; const identity2: <T>(arg: T) => T = (arg) => { return arg; };
泛型接口
/* -------------泛型接口-------------*/ interface ColumnProps<T> { key: number | string; title: string; dataIndex: keyof T; // 约束 dataIndex 值需为引用泛型 T 中的属性 } interface ITableItem { key: number | string; name: string; address: string; age: number; } const columns: Array<ColumnProps<ITableItem>> = [ { title: '姓名', dataIndex: 'name', key: 'name', }, ];
泛型类
/*--------------泛型类---------------*/ class Person<T> { love: T; say: (arg: T) => T; } let myFn: IGeneric<number> = fn; myFn(13); //13 let me = new Person<string>(); me.love = 'TS'; // me.love = 520; // ERROR me.say = function(love: string){ return `my love is ${love}`; }
泛型约束
泛型可以通过 extends 一个接口来实现泛型约束,写法如:
<泛型变量 extends 接口>
<T, K extends keyof T> //K为传入的T上已知的属性, interface IArray { length: number } function logIndex<T extends IArray>(arg: T): void { for (let i = 0; i < arg.length; ++i) { console.log(i) } } let arr = [1, 2, 3] // logIndex<number>(arr) // 报错 logIndex<number[]>(arr) // 允许 logIndex(arr) // 自动类型推导,允许
泛型应用场景之一
/*-------------应用场景start---------------------------*/ interface ColumnProps<T> { key: number | string; title: string; dataIndex: keyof T; // 约束 dataIndex 值需为引用泛型 T 中的属性 } interface ITableItem { key: number | string; name: string; address: string; age: number; } interface TableProps { dataSource: ITableItem[]; columns: Array<ColumnProps<ITableItem>>; } const MyTable = (props: TableProps) => { const { dataSource, columns } = props; return <Table dataSource={dataSource} columns={columns} />; }; const ApplcationMod = () => { const dataSource = [ { key: '1', name: '金城武', age: 32, address: '西湖区湖底公园1号', }, { key: '2', name: '吴彦祖', age: 42, address: '西湖区湖底公园1号', }, ]; const columns: Array<ColumnProps<ITableItem>> = [ { title: '姓名', dataIndex: 'name', key: 'name', }, { title: '年龄', dataIndex: 'age', key: 'age', }, { title: '住址', dataIndex: 'address', key: 'address', }, ]; return ( <div> <h3>泛型应用场景</h3> <MyTable dataSource={dataSource} columns={columns} /> </div> ); };
总结
加载全部内容