定义:
泛型是指在定义函数、接口或类的时候,不预先指定具体类型,而是在使用的时候再指定类型
语法:
泛型的语法是 <>
里写类型参数,一般可以用 T
来表示
泛型中的 T
就像一个占位符、或者说一个变量,在使用的时候可以把定义的类型像参数一样传入,它可以原封不动地输出
泛型的好处:
为什么需要泛型?
不使用泛型:
function identity(arg: number): number {
return arg;
}
function identity(arg: string): string {
return arg;
}
或者使用any
类型来定义函数:
function identity(arg: any): any {
return arg;
}
使用any类型会导致这个函数可以接收任何类型的arg参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的
因此,我们需要一种方法使返回值的类型与传入参数的类型是相同的
function identity<T>(arg: T): T {
return arg;
}
// T帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型
定义了泛型函数后,两种方法使用
(1)传入所有的参数,包含类型参数:
let output = identity<string>("myString"); // type of output will be 'string'
(2)利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型:
let output = identity("myString"); // type of output will be 'string'
泛型接口允许你在定义接口的时候不具体指明某些类型,并且在实现接口或者使用接口的时候再指定这些类型
泛型接口可以用来约束对象的形状、数组内容、函数签名等多种用途。
定义:
interface Identity<T> {
value: T;
getMessage(): string;
}
使用:
// 使用接口并指定 T 为 number 类型
let numberIdentity: Identity<number> = {
value: 42,
getMessage() {
return `Value is a number: ${this.value}`;
}
};
// 使用接口并指定 T 为 string 类型
let stringIdentity: Identity<string> = {
value: 'Hello',
getMessage() {
return `Value is a string: ${this.value}`;
}
};
定义:
interface GenericFn<T> {
(arg: T): T;
}
使用:
// 实现接口的函数 - 返回值类型与入参数类型相同
const identity: GenericFn<number> = (arg) => arg;
定义:
interface Dictionary<T> {
[key: string]: T;
}
使用:
// 字典创建时确定其值应为字符串类型
let keys: Dictionary<string> = {
key1: 'value1',
key2:'value2',
};
// 字典创建时确定其值应为数组类型
let objectList: Dictionary<object[]> = {
list1: [{}, {}],
list2: [{}, {}, {}]
};
定义:
interface Pair<K, V> {
key: K;
value: V;
}
使用:
let pair: Pair<string, number> = {
key: 'age',
value: 35
};
泛型类是具有一个或多个泛型类型参数的类,这些类型参数在类实例化的时候被指定
定义:
class GenericClass<T> {
// property 的类型为 T
value: T;
// 构造函数参数的类型为 T
constructor(value: T) {
this.value = value;
}
// 方法返回值的类型为 T
getValue(): T {
return this.value;
}
}
使用:
// 使用字符串类型实例化类
const stringInstance = new GenericClass<string>('Hello, TypeScript!');
console.log(stringInstance.getValue()); // 输出: Hello, TypeScript!
// 使用数字类型实例化类
const numberInstance = new GenericClass<number>(42);
console.log(numberInstance.getValue()); // 输出: 42
// 使用数组类型实例化类
const arrayInstance = new GenericClass<string[]>(['apple', 'banana', 'cherry']);
console.log(arrayInstance.getValue()); // 输出: ['apple', 'banana', 'cherry']
定义:
class KeyValueClass<K, V> {
key: K;
value: V;
constructor(key: K, value: V) {
this.key = key;
this.value = value;
}
getKeyValue(): string {
return `Key: ${this.key}, Value: ${this.value}`;
}
}
使用:
// 使用类
const keyValuePair = new KeyValueClass<number, string>(1, 'One');
console.log(keyValuePair.getKeyValue()); // 输出: Key: 1, Value: One
定义一个接口来描述约束条件:
// 创建一个包含 .length 属性的接口
interface Lengthwise {
length: number;
}
// 使用这个接口和extends关键字来实现约束:
function loggingIdentity<T extends Lengthwise>(arg: T): T{
return arg;
}
现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:
loggingIdentity(3); // Error, number doesn't have a .length property
我们需要传入符合约束类型的值,必须包含必须的属性:
loggingIdentity({length: 10, value: 3});
你可以声明一个类型参数,且它被另一个类型参数所约束
定义:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
使用:
const obj = {
name: 'Alice',
age: 25,
hasPet: false
};
// 正常使用
const name = getProperty(obj, 'name'); // 正确,name 的类型被推断为 string
const age = getProperty(obj, 'age'); // 正确,age 的类型被推断为 number
// 错误使用
const weight = getProperty(obj, 'weight'); // 错误!'weight' 不存在于obj类型上
在 TypeScript 中利用泛型来使用类类型,你可以创建一个函数,其参数或返回值的类型可以是一个类构造器(类的静态部分)或类的实例(类的实例部分)
function createInstance<T>(c: { new (): T }): T {
return new c();
}
例如,假设我们有一个类 Animal 和一个工厂函数,我们想让这个函数返回一个 Animal 类型的实例对象:
class Animal {
name: string;
constructor(name: string) { this.name = name; }
move(meters: number) {
console.log(`${this.name} moved ${meters}m.`);
}
}
// createInstance 函数可以接受任何具有单个字符串参数构造函数的类
function createInstance<T>(c: { new (name: string): T }): T {
return new c('Animal Name');
}
let animal: Animal = createInstance(Animal);
animal.move(5); // 输出: Animal Name moved 5m.
你也可以在泛型类中使用类类型,如下所示:
class BeeKeeper {
hasMask: boolean = true;
}
class ZooKeeper {
nameTag: string = 'Mikle';
}
class Animal {
numLegs: number = 4;
}
class Bee extends Animal {
keeper: BeeKeeper = new BeeKeeper();
}
class Lion extends Animal {
keeper: ZooKeeper = new ZooKeeper();
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
// 创建 Lion 实例
createInstance(Lion).keeper.nameTag; // 类型检查 OK
// 创建 Bee 实例
createInstance(Bee).keeper.hasMask; // 类型检查 OK