软件工程中,我们不仅要创建一致的定义良好的
API,同时也要考虑可重用性。组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时提供了十分灵活的功能。 泛型就是解决类、接口、方法的复用性,以及对不特定数据类型的支持
在代码编译阶段,我们在写代码不确定这个A变量的类型,虽然不确定,但想对不确定的A变量进行相同的处理。
这里的“类型”:不仅指数据类型,还可以指变量的类,还可以指变量的接口等等。
在代码运行阶段,这个变量肯定是已知的某个类型,这个时候,所谓的泛型才变成了固定的类型。
而泛型变量呢,是我们用来暂时取代变量A的类型的变量。
泛型变量的别称,类型变量。
类型不确定的情况,都是在函数传参的时候发生的,叫做泛型函数。
相应的,还有泛型类、泛型接口
1. 常见泛型变量
常见泛型变量代表的意思:
- T(Type): 表示一个
TypeScript类型 - K(Key):表示对象中的键类型
- V(Value):表示对象中的值类型
- E(Element):表示元素类型
这是约定俗成的用法。
2. 泛型函数
// 要求传入的参数类型和返回类型一致// T 表示泛型,具体什么类型是调用这个方法的时候决定的function getData<T>(value: T): T {return value;}getData({ name: 'konsoue' })// 使用函数是,可以提前告知函数,泛型变量的值getData<number>(123);getData<string>('123');
现在我们添加了一个泛型变量 T 到函数中,这个变量允许我们获取,用户在使用这个函数时参数的类型。
这个函数里我们将 T 作为参数的类型和返回值的类型。
function test<T, D>(val1: T, val2: D): D {return val2;}test(123, 22);test<number, string>(123, '22');
// 交换函数function swap<T, U>(tuple: [T, U]): [U, T] {return [tuple[1], tuple[0]];}swap([1, '2'])
3. 泛型类
class MinClass<T> {public list: Array<T> = [];add(item: T): void {this.list.push(item);}remove(item: T): void {this.list.splice(this.list.indexOf(item), 1);}}let test = new MinClass();test.add(2);test.add({ name: 'konsoue' });test.add('5');let num = new MinClass<number>();num.add(55);num.add(1)
明白了,上面的可以这样做,就能理解下面这段代码了
- 把类作为参数来约束数据传入类型 ```typescript class User { userName: string | undefined; password: string | undefined; }
class MysqlDb { add(user: User): boolean { return true; } } let user = new User(); user.userName = ‘张三’; user.password = ‘123456’;
let db = new MysqlDb(); db.add(user);
- 把类当作参数的泛型类```typescript// 操作数据库的泛型类class MysqlDb<T> {add(info: T): boolean {return true;}}// 想给 user 表添加数据let db = new MysqlDb<User>();db.add(user);let db2 = new MysqlDb<ArticleCate>();db2.add(article);
4. 泛型接口
// 普通函数接口interface ConfigFn {(value1: string, value2: string): [string, string];}const setData: ConfigFn = function (value1, value2) {return [value1, value2];}// 泛型接口写法一interface ConfigFn {<T>(value1: T, value2: T): [T, T];}const setData: ConfigFn = function (value1, value2) {return [value1, value2];}// 泛型接口写法二interface ConfigFn<T> {(value1: T, value2: T): [T, T];}const setData: ConfigFn<string> = function (value1, value2) {return [value1, value2];}
5. 约束泛型
我们可以对泛型进行一些约束,从而约束传进来的数据的类型。
interface IWithLength {length: number;}// 限制传入的泛型类型具备 length 属性function echoWithLength<T extends IWithLength>(arg: T): T {console.log(arg.length);return arg;}const str = echoWithLength('str');const obj = echoWithLength({ length: 10 })const arr = echoWithLength([1, 2, 3])
6. 泛型工具类型
为了方便开发者, TypeScript 内置了一些常用的工具类型,比如 Partial 、 Required 、 ReadOnly 、 Record 和 ReturnType 等。
6.1 关键字
typeof 和 type
typeof 操作符可以用来获取一个变量声明或对象的类型。
interface IUserProps {vType: 'userProps';name: string;email: string;}function isUserProps(x: any): x is IUserProps {return x.vType === 'userProps';}class UserProps {name: string;email: string;vType: 'userProps';constructor(name: string, email: string) {this.name = name;this.email = email;this.vType = 'userProps';}}const userProps: IUserProps = new UserProps('konsoue', 'xx@qq.com')type copyIuserProps = typeof userProps // copyIuserProps 就是 IUserPropsconst obj: copyIuserProps = {} // 报错。类型“{}”缺少类型“IUserProps”中的以下属性: vType, name, email
keyof
用来获取一个对象中的所有
key值
interface Person {name: string;age: number;}type K1 = keyof Person; // "name" | "age"type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"type K3 = keyof { [x: string]: Person }; // string | numberconst a: K1 = { name: 'John', age: 15 } // xconst a: K1 = 'name'; // √
in
用来遍历枚举类型
type Keys = "a" | "b" | "c"type Obj = {[p in Keys]: any} // -> { a: any, b: any, c: any }
extends
有时候我们定义的泛型不想过于灵活或者说想继承某些类等,可以通过 extends 关键字添加泛型约束
interface ILengthwise {length: number;}function loggingIdentity<T extends ILengthwise>(arg: T): T {console.log(arg.length);return arg;}loggingIdentity(3); // Error, number doesn't have a .length property// 需要传入符合约束类型的值,必须包含必须的属性loggingIdentity({length: 10, value: 3});
infer
在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
以上代码中 infer R 就是声明一个变量来承载传入函数签名的返回值类型,简单说就是用它取到函数返回值的类型方便之后使用。
6.2 Partial
Partial<T> 的作用是将某个类型里的属性全部变为可选项。
源码中 Partial 的定义
type Partial<T> = {[P in keyof T]?: T[P];};
使用例子
interface Todo {title: string;description: string;}function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {return { ...todo, ...fieldsToUpdate };}const todo1 = {title: "organize desk",description: "clear clutter",};const todo2 = updateTodo(todo1, {description: "throw out trash",});/*fieldsToUpdate 的类型为 Partial<Todo>, 即{title?: string | undefined;description?: string | undefined;}*/
6.3 Required
Required<T>将传入的属性变为必选项,,源码如下
type Required<T> = {[P in keyof T]-?: T[P]};
-?:减掉可选+?:加上可选。+ 可省略
