泛型函数是最常见的泛型用法。你可以使用泛型来定义一个函数,使其能够处理多种类型,而不局限于单一类型。
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString"); // 使用类型参数
let output2 = identity<number>(42); // 使用类型参数
在上面的例子中,identity
函数有一个类型参数 T
,它表示任意类型。调用函数时,我们可以指定类型参数,如 identity<string>
,也可以让 TypeScript 自动推断类型参数。
你可以在函数内部使用泛型变量,这样可以保证函数处理的类型一致。
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length); // 泛型数组有length属性
return arg;
}
let array = loggingIdentity<number>([1, 2, 3]);
在这个例子中,泛型变量 T
被用作数组类型,这样函数既可以处理 number[]
,也可以处理 string[]
等其他类型的数组。
接口也可以使用泛型,使得接口中的属性类型可以灵活地变化。
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
在这个例子中,GenericIdentityFn
接口定义了一个泛型函数类型。myIdentity
函数实现了这个接口,并且类型参数 T
被指定为 number
。
类也可以使用泛型,使得类中的属性和方法可以根据不同的类型参数变化。
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
在这个例子中,GenericNumber
类使用了泛型 T
,并且在实例化时指定了类型参数 number
。
有时候我们希望泛型类型变量满足某些条件,可以通过泛型约束来实现。
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在 `length` 属性是必需的
return arg;
}
loggingIdentity({ length: 10, value: "hello" });
在这个例子中,T
被约束为 Lengthwise
类型,这样 loggingIdentity
函数中的 arg
参数必须有 length
属性。
你可以在泛型中使用另一个类型参数。
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3 };
let value = getProperty(x, "a"); // 1
在这个例子中,getProperty
函数有两个类型参数 T
和 K
,K
被约束为 T
的键,确保 key
参数是 obj
对象的有效键。
TypeScript 提供了一些内置的泛型工具类型,用于在类型层面进行操作。
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
interface Person {
name: string;
age: number;
}
type PartialPerson = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
在这个例子中,Partial
和 Readonly
是两个内置的泛型工具类型,用于将接口的所有属性变为可选和只读。