泛型程序设计是一种编程风格或编程范式,它允许在程序中定义形式类型参数,然后在泛型实例化时使用实际类型参数来替换形式类型参数。通过泛型,我们能够定义通用的数据结构或类型,这些数据结构或类型仅在它们操作的实际类型上有差别。泛型程序设计是实现可重用组件的一种手段。
1. 泛型简介
我们先看一个函数identity,恒等函数,它的返回值永远等于传入的参数
function identity<T>(arg: T): T {
return arg;
}
此例中,T是identity函数的一个类型参数,它能够捕获identity函数的参数类型并用作返回值类型。从identity函数的类型注解中我们能够观察到,传入参数的类型与返回值类型是相同的类型,两者均为类型T。我们称该版本的identity函数为泛型函数。
在调用identity泛型函数时,我们能够为类型参数T传入一个实际类型。示例如下:
// 能够推断出 'foo' 的类型为 'string'
const foo = identity<string>('foo');
2. 形式类型参数
泛型类型参数能够表示绑定到泛型类型或泛型函数调用的某个实际类型。在类声明、接口声明、类型别名声明以及函数声明中都支持定义类型参数。泛型形式类型参数列表定义的具体语法如下所示:
<TypeParameter, TypeParameter, ...>
TypeParameter表示形式类型参数名,形式类型参数需要置于“<”和“>”符号之间。当同时存在多个形式类型参数时,类型参数之间需要使用逗号“,”进行分隔。eg
function assign<T, U>(target: T, source: U): T & U {
// ...
}
3. 实际类型参数
在引用泛型类型时,可以传入一个实际类型参数作为形式类型参数的值,该过程称作泛型的实例化。eg:
function identity<T>(arg: T): T {
return arg;
}
identity<number>(1);
identity<Date>(new Date());
4. 泛型约束
在泛型的形式类型参数上允许定义一个约束条件,它能够限定类型参数的实际类型的最大范围。我们将类型参数的约束条件称为泛型约束。定义泛型约束的语法如下所示:
<TypeParameter extends ConstraintType>
TypeParameter表示形式类型参数名;extends是关键字;ConstraintType表示一个类型,该类型用于约束TypeParameter的可选类型范围。eg:
interface Point {
x: number;
y: number;
}
function identity<T extends Point>(x: T): T {
return x;
}
// 正确
identity({ x: 0, y: 0 });
identity({ x: 0, y: 0, z: 0 });
// 编译错误!类型 '{ x: number; }' 不能赋值给类型 Point
identity({ x: 0 });
在泛型约束中,约束类型允许引用当前形式类型参数列表中的其他类型参数。
//泛型约束引用类型参数
<T, U extends T>
5. 泛型函数
若一个函数的函数签名中带有类型参数,那么它是一个泛型函数。泛型函数中的类型参数用来描述不同参数之间以及参数和函数返回值之间的关系。泛型函数中的类型参数既可以用于形式参数的类型,也可以用于函数返回值类型。
语法如下
// 调用前面
<T>(x: T): T
// 构造前面
new <T>(): T[];
举个栗子
//f0函数接受任意类型的参数x,并且返回值类型与参数类型相同。
function f0<T>(x: T): T {
return x;
}
const a: string = f0<string>('a');
const b: number = f0<number>(0);
6. 泛型接口
若接口的定义中带有类型参数,那么它是泛型接口。在泛型接口定义中,形式类型参数列表紧随接口名之后。泛型接口定义的语法如下所示:
interface MyArray<T> extends Array<T> {
first: T | undefined;
last: T | undefined;
}
在引用泛型接口时,必须指定实际类型参数,除非类型参数定义了默认类型。示例如下:
const a: Array<number> = [0, 1, 2];
“Array<T>”是TypeScript内置的泛型数组类型,它的定义如下所示(TypeScript部分代码):
interface Array<T> {
/**
* Gets or sets the length of the array. This is a number one higher than the highest index in the array.
*/
length: number;
/**
* Returns a string representation of an array.
*/
toString(): string;
/**
* Returns a string representation of an array. The elements are converted to string using their toLocaleString methods.
*/
toLocaleString(): string;
/**
* Removes the last element from an array and returns it.
* If the array is empty, undefined is returned and the array is not modified.
*/
pop(): T | undefined;
/**
* Appends new elements to the end of an array, and returns the new length of the array.
* @param items New elements to add to the array.
*/
push(...items: T[]): number;
// .....
}
在“Array<T>”泛型接口类型中,类型参数T表示数组元素类型。在接口中的方法签名和索引签名中都引用了类型参数T。例如,reverse方法会反转数组元素,它的返回值仍为由原数组元素构成的数组。因此,reverse方法的返回值类型是“T[]”,即由原数组元素类型构成的数组类型。
7. 泛型类型别名
若类型别名的定义中带有类型参数,那么它是泛型类型别名。
在泛型类型别名定义中,形式类型参数列表紧随类型别名的名字之后。泛型类型别名定义的语法如下所示:
type Nullable<T> = T | undefined | null;
eg:
type Container<T> = { value: T };
const a: Container<number> = { value: 0 };
const b: Container<string> = { value: 'b' };
8. 泛型类
若类的定义中带有类型参数,那么它是泛型类。
在泛型类定义中,形式类型参数列表紧随类名之后。定义泛型类的语法如下所示:
class Container<T> {
constructor(private readonly data: T) {}
}
const a = new Container<boolean>(true);
const b = new Container<number>(0);
网友评论