typescript-泛型—上
泛型程序设计是一种编程风格或编程范式,允许在程序中定义形式类型参数,然后再泛型实例化时候使用实际类型参数来替代形式类型参数,通过泛型,可以定义通用的数据结构或类型,这种数据结构或类型仅仅再它们操作的实际类型上有差别。
function identiy<T>(arg:T): T {
return arg
}
T是identity函数的一个类型参数,能够捕获identtity函数参数类型并用作返回值类型,传入的参数的类型与返回值类型是相同的类型。两者均为类型t,将这种函数称为泛型函数。
function identity<T>(arg: T):T {
return arg
}
const foo = identity<string>('foo')
const bar = identity<string>(true) // 类型为true的参数不能赋值。
在大部分情况下,程序中不需要显示的指定类型参数的实际类型,typescript编译器能够更具函数调用的实际参数自动推断出类型参数的实际类型。
形式类型参数
形式类型参数声明
泛型类型参数能够绑定到泛型类型或者泛型函数调用的某个实际类型,再类声明,接口声明,类型别名给声明以及函数声明中都支持定义类型参数。<TypeParameter, TypeParameter,...>
在这个语法中,typeParamter表示形式类型参数名,形式类型参数需要放到<>之间,当存在形式参数类型的时候,类型参数之间要用逗号隔开。
function assign<T,U> (target: T, source: U): T & U {
// ...
}
类型参数默认类型
<T = DefaultType>
<T = boolean>
<T, U = T>
可选的类型参数
如果一个类型参数没有定义默认类型,那么他是一个必选的类型参数,如果一个形式类型参数定义了默认类型,那么他是一个可选的类型参数,在形式类型参数列表中,必选类型参数不允许出现在可选类型参数之后。
<T = boolean, U> // 错误
<T, U = boolean> // 正确
编辑器从左到右的顺序依次解析并设置类型参数的默认类型,如果一个类型参数的默认类型引用的左侧声明的类型参数,就灭有问题,如果一个类型参数的默认类型引用了右侧声明的类型参数,会发生编译错误。
<T = U, U = boolean> // 错误
<T = boolean, U = T> // 正确
实际类型参数
在引用泛型类型的时候,可以传入一个实际类型参数作为心事类型参数的值,这个过程称为泛型的实例化,传入实际参数的语法<Type, Type, ...>
function identity<T> (arg:T) :T {
return arg
}
identity<number>(1)
identity<Date>(new Date())
function f<T, U = boolean>() {}
f<string>()
f<string,string> ()
泛型约束声明
在泛型的形式类型上允许定义一个约束条件,能够限定类型参数的实际类型的最大范围,将类型参数的约束条件称为泛型约束<Typeparamter extends ConstrainType>
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})
identiey({x: 0})
泛型约束引用类型参数
泛型约束中,泛型类型允许引用当前形式类型参数列表中的其他类型参数。<T, U extends T>
基约束
每个类型参数上都有一个基约束,和是否在形式类型参数上定义了泛型约束无关,类型参数的实际类型一定是其基约束的子类型,对于任意的类型参数T, 与基约束的计算规则如下
- 如果类型参数T声明了泛型约束,且泛型约束为另一个类型参数U, 则泛型约束的基约束为类型参数U
<T extends U> // 类型参数T的基约束为类型参数U
- 如果类型参数T声明了泛型约束,且泛型约束为某一具体类型Type,那么类型参数T的基约束为类型Type
<T extends boolean>
- 如果类型参数T没有声明泛型约束,那么类型参数T的基约束为空对象类型字面量“{}”。除了undefined类型和null类型外,其他任何类型都可以赋值给空对象类型字面量
<T> // 类型参数T的基约束为"{}"类型
常见错误
interface Point {
x: number;
y: number;
}
function f<T extends Point>(arg: T): T {
return {x: 0, y: 0} // 编译错误, 类型`{x: number; y: number}`不能赋值给类型T
}
泛型函数
若一个函数的函数签名中有类型签名,那么他是一个泛型函数,泛型函数中的类型参数中用来描述不同参数之间以及参数和函数返回值之间的关系,泛型函数中的类型参数即可以用于形式参数和类型,也可以用于函数返回值类型
泛型函数定义
函数签名为调用签名和构造签名,这两种签名都支持定义类型参数。<T>(x: T): T
定义泛型构造函数的语法new <T>(): T[]
function f0<T>(x: T): T {
return x;
}
const a: string = f0<string>('a')
const b: string = f0<number>(0)
function f3<T, U>(a: T[], f(x: T) => U): U[] {
return a.map(f)
}
const a:boolean[] = f3<number, boolean>([0, 1, 2], n => !!n) ;
function f0<T>(x: T): T{
return x
}
const a: string = f0('a')
const b = f0('b') //推断出的实际类型参数为'a'
泛型函数的类型参数是用来关联多个不同值的类型的,如果一个类型参数只在函数签名中出现一次,说明它和其他值是没有关系的,不需要使用类型参数。
泛型接口
interface myArray<T> extends Array<T> {
first: T | undefined;
last: T | undefined;
}
const a: Array<number> = [0, 1, 2]
类型泛型别名
type Nullable<T> = T | undefined | null
type Container<T> = {value: T}
const a: Container<number> = <value: 0>;
const b: Container<string> = {value: 'b'}
type Tree<T> = {
value: T;
left: Tree<T> | null
right: Tree<T> | null
}
const tree: Tree<number> = {
value: 0;
left: {
vale: 1;
left: {
value: 3,
left: null,
right: null
},
right: {
value: 4,
left: null,
right: null
}
},
right: {
value: 2,
left: null,
right: null
}
}
泛型类
若类的定义中带有类型参数,那么它是泛型类
在泛型类的定义中形式类型参数列表在类名之后,定义泛型类的语法如下
class Container<T> {
constructor(private readonly data: T) {}
}
const a = new Container<boolean>(true)
const b = new Container<number>(0)
interface A<T> {
a: T;
}
class Base<T> {
b?: T;
}
class Derived<T> extends Base<T> implements A<T> {
constructor(public readonly a: T) {
super()
}
}
局部类型
function f<T> {
enum E {
A,
B
}
class C {
x: string | undefined;
}
interface I<T> {
x: T;
}
type A = E.A | E.B
}
function f(x: boolean) {
if (x) {
interface T {
x: number;
}
const v: T = { x: 0 };
} else {
interface T {
x: string;
}
const v: T = { x: 'foo' };
}
}