背景:实现一个打印函数
function log(value: string): string {
console.log(value);
return value;
}
-
需求+1:函数也要可以接受字符串数组
这里我们可能想到了用函数重载。
function log(value: string): string; function log(value: string[]): string[]; function log(value: any): any { console.log(value); return value; }
-
需求+2:这个函数会返回任何传入它的值,且类型保持一致。
我们可以用any
类型,实现传入任何类型
。但会丢失部分信息(类型之间的约束关系),忽略了输入参数的类型和返回值的类型必须是一致的
。function log(value: any) { console.log(value); return value; }
此时,我们用 类型变量,它是一种特殊的变量,只用于表示类型而不是值。
function log<T>(value: T): T { console.log(value); return value; }
变量
T
捕获传入参数的类型,之后我们就可以使用这个T
类型,再将T
作为返回值类型。
此时,这个版本的 log 函数叫做 泛型。
什么是泛型
不预先确定的数据类型,具体类型在使用时才可以确定。
-
两种调用方式
-
指明类型
log<string[]>(["a", "b", "c"]);
-
类型推断
log(["a", "b", "c"]);
-
-
泛型类型
我们不仅可以用泛型定义函数,还可以定义函数类型。
可以将泛型类型看成只定义类型的传参,帮助理解。function log<T>(value: T): T { console.log(value); return value; } type MyLog = <T>(value: T) => T; let myLog: MyLog = log; // 还可以使用不同的泛型参数名,只要数量和使用方式能对应上。 type ULog = <U>(value: U) => U; // 还可以使用带有调用签名的对象字面量来定义泛型函数: type OLog = { <T>(value: T): T };
-
泛型接口
interface MyLog { // 泛型仅仅约束函数 <T>(value: T): T; } // 泛型约束了整个接口,实现时必须指定类型,或在接口定义是指定默认类型 interface MyLog<T = string> { (value: T): T; } let ilog: ILog<number> = log;
-
泛型类
泛型类指的是实例部分的类型,所以类的 静态属性 不能使用这个泛型类型。
class Gen<T> { constructor() {} run(value: T): T { console.log(value); return value; } } let gen1 = new Gen<number>(); gen1.run(1); gen1.run("1"); // error! let gen2 = new Gen(); // 默认 any gen2.run(1); gen2.run("1");
-
泛型约束
我们定义一个接口来描述约束条件。
使用这个接口和extends
关键字来实现约束:interface Length { length: number; } function log<T extends Length>(value: T) { console.log(value.length); return value; } log("1"); log([1, 2, 3]); log({ length: 1 }); log(2); // error!
泛型有什么好处呢?
- 函数和类可以轻松支持多种类型,增强程序的扩展性。
- 不必写多条函数重载,冗长的联合类型声明,增加代码可读性。
- 灵活控制类型之间的约束。
TypeScript 入门系列
- Hello TypeScript(01)-- 环境搭建
- Hello Typescript(02)-- 枚举类型
- Hello Typescript(03)-- 对象类型接口
- Hello Typescript(04)-- 函数类型接口、混合类型接口、类接口
- Hello Typescript(05)-- 函数
- Hello Typescript(06)-- 类
- Hello Typescript(07)-- 类与接口的关系
- Hello Typescript(08)-- 泛型
- Hello Typescript(09)-- 类型推断、类型兼容性、类型保护
- Hello Typescript(10)-- 交叉类型、联合类型、索引类型、映射类型、条件类型