typescirpt泛型—下
联合类型
联合类型字面量
联合类型由两个或者两个以上的成员类型构成,各个成员类型之间是通过竖线分隔
type NumericType = number | bigint
type T = boolean | string[] | {x: number} | (() => void)
联合类型的类型成员
属性签名
interface Circle {
area: number;
radius: number
}
interface Rectangle {
area: number;
width: number;
height: number;
}
type Shape = Circle | Rectangle
area的类型为Circle类型中的area属性和Rectangle类型中的area属性。
interface Circle {
area: bigint
}
interface Rectangle {
area: number
}
declare const s: Circle | Rectangle
s.area // bigint | number
如果联合属性签名在某个成员类型中是可选属性签名,那么在该属性签名在联合类型中也是可选属性签名。
interface Circle {
area: bigint;
}
interface Rectangle {
area?: number
}
declare const s: Circle | Rectangle
s.area // bigint | number | undefined
索引签名
索引签名包括两种,字符串索引签名和数值索引签名。
如果联合类型中每个成员都包含字符串索引签名,那么该联合类型也有了字符串索引签名。
interface T0 {
[prop: string]: number;
}
interface T1 {
[prop: string]: bigint
}
type T = T0 | T1
interface T0T1{
[prop: string]: number |bigint;
}
T相当于接口类型T0T1
调用签名与构造签名
如果联合类型为每个成员类型都包含了相同参数列表的调用签名,那么联合类型也拥有了该调用签名。返回值为每个成员类悉尼港中调用签名返回值类型的联合类型。
interface T0 {
(name: string): number
}
interface T1 {
(name: string): bigint
}
type T = T0 | T1
interface T0T1 {
(name: string): number | bigint
}
如果联合类型中每个成员都包含相同参数列表的构造签名,那么该联合类型也拥有了构造签名,其返回值类型为每个成员类型中构造签名返回值类型的联合类型
interface T0 {
new (name: string): Date
}
interface T1 {
new (name: string): Error
}
type T = T0 | T1
interface T0T1 {
new (name: stirng): Date | Error
}
交叉类型
交叉类型字面量
交叉类型由两个或多个类型构成。
interface Clickable {
click(): void
}
interface Focusable {
focus(): void
}
type T = Clickable & Focusable
若一个值既是Clickable类型又是Focusable类型,那么我们说该值的类型为交叉类型
成员类型的运算
和联合类型相似,如果交叉类型中存在多个相同的成员类型,那么相同的成员类型被合并为单一的成员类型
interface Clickable {
click(): void
}
interface Focusable {
focus(): void
}
type T0 = Clickable & Focusable;
type T1 = Focusable & Clickable;
成员类型的顺序不影响交叉类型的结果类型。当交叉类型设计调用签名或构造函数重载的时候,成员类型的顺序决定了重载的顺序。从而影响重载签名的解析顺序
interface Clickable {
register(x: any): void
}
interface Focusable {
register(x: string): boolean
}
type ClickableAndFocusable = Clickable & Focusable
type FocusableAndFocusable = Focusable & Clickable
function foo (
clickFocus: ClickableAndFocusable,
focusClick: FocusableAndFocusable
) {
let a: void = clickFocus.register('foo')
let b: boolean = focusClick.register('foo')
}
原始类型
交叉类型通常和对象类型一起使用,虽然在交叉类型中也允许使用原始类型成员,但是结果类型将成为never类型。
type T = boolean & number & string
交叉类型的类型成员
属性签名
只要交叉类型中的任意一个成员类型包含了属性签名M,那么交叉类型也包含属性签名M
interface A {
a: boolean
}
interface B {
b: string
}
A & B = {a:boolean; b: string}
索引签名
interface A {
[prop: stirng]: string
}
interface B {
[prop: number]: string
}
A&B
{
[prop: string]: string,
[prop: number]: string
}
interface A {
[prop: stirng]: {a: boolean}
}
interface B {
[prop: string]: {b: boolean}
}
A&B
{
[prop: string]: {a: boolean} & {b: boolean}
}
调用签名与构造签名
interface A {
(x: number): number;
}
interface B {
(x: string): string;
}
A & B
{
(x: boolean): boolean;
(x: string): boolean
}
B & A
{
(x: string): string
(x: boolean): boolean
}
优先级: & > |
索引类型
keyof Type
interface Point {
x: number;
y: number
}
type T = keyof Point // "x" | "y"
索引类型查询
javascript中的对象是键值对的数据结构,只允许字符串和symbol值作为对象的键,索引类型查询获取的是对象的键的类型,只有此索引类型查询结果类型为"string | symbol",但是数组类型十分常用而且索引值的类型为number类型,因此编译器将number类型纳入了索引类型查询的结果类型范围。
如果类型t中包含字符串缩影签名,那么string类型和number类型添加到keyof T
interface T {
[prop: string]: number
}
// string | number
type KeyofT = keyof T
如果类型T中包含属性名类型为"unique symbol"的属性,那么将"unique symbol"类型添加到类型keyof T,如果想要在对象类型中声明属性为symbol类型的属性,那么属性名的类型必须为unique symbol类型。不允许symbol类型。
const s: unique symbol = Symbol()
interface T {
[s]: boolean
}
// string | number | symbol
type keyofT = keyof T;
interface T {
0: boolean;
a: stirng;
b(): void
}
// 0 | 'a' | 'b'
type KeyOfT = keyof T
当对any使用索引类型查询的时候,结果类型固定为联合类型"string | number | symbol"
type KeyofT = keyof any; // string | number | symbol
当unknown类型使用索引类型查询的时候,结果类型固定为never类型
type KeyofT = keyof unknown //never
索引访问类型
const s: unique symbol = Symbol()
enum E {
A = 10
}
type T = {
0: string;
x: boolean;
[E.A]: number;
[s]: bigint
}
type TypeOfNumberLikeName = T[0] //string
type TypeofStringLikeName = T['x'] // string
type TypeofEnumName = T[E.A] // number
type TypeofSymbolName = T[typeof s] // bigint
映射对象类型
映射对象是一个类型运算符,可以编译联合类型并以该联合类型的类型成员作为属性名类型来构造一个对象类型,映射对象类型语法为{readonly [p in k]? : T}
k表示要遍历的类型,由于遍历的结果类型作为对象属性名类型,因此类型k必须能够赋值联合类型string|number|symbol
, 因此只有这种类型的值才能作为对象的键,p表示类型变量,代表每次遍历出来的成员类型,T是任意类型,表示对象属性的类型,并且在类型T中允许使用类型变量P。
映射对象类型的运算结果是一个对象类型。映射对象类型的核心是它能遍历类型K的所有类型成员,并针对每一个成员P都将它映射为类型T。
type K = 'x' | 'y'
type T = number
type MappedObjectType = { readonly [p in K]? : T}
// MappedObjectType = {
// readonly x?: number;
// readonly y?: number;
// }
type MappedObjectType = { [p in 'x']: boolean}
// MappedObjectType = {
// x: boolean
// }
type MappedObjectType = { [p in 0]: boolean}
// MappedObjectType = {
// 0: boolean
// }
const s: unique symbol = Symbol()
type MappedObjectType = { [P in typeof S]: boolean}
// MappedObject = {
// [s]: boolean
// }
type MappedObjectType = { [P in string]: boolean}
// MappedObjectType = {
// [x: string]: boolean
// }
type T = {a: stirng; b: number}
type M = { [P in keyof T]J: boolean }
// M = {
// a: boolean;
// b: boolean
// }
type M = { [P in keyof T]: T[p]}
M = {
a: string;
b: number
}
type OptionalT = {[P in keyof T]? T[P]}
// OptionalT = {
// a?: string;
// b?: string
// }
type Partial<T> = {
[p in keyof T]?: T[p]
}
type OptionalT = Partial<T>
// OptionalT = {
// a?: string;
// b?: number;
// }
type ReadonlyT = { readonly [p in keyof T]: T[p]}
type Readonly<T> = {
readonly [p in keyof T]: T[p]
}
type ReadonlyT = Readonly<T>
// ReadonlyT = {
// readonly a: string;
// readonly b: number
// }
同态映射对象类型
映射后的对象类型结构与源对象类型T的结构完全一致,这种映射对象类型称为同态映射对象类型。
结构为readonly [p in keyof T]?: X
修饰符拷贝
同态映射对象类型的特征: 新的对象类型会默认拷贝源对象类型中所有属性的readonly修饰符和?修饰符
type Pick<T, K extends keyof T> = { // 非同态映射对象类型
[p in k]: T[p]
}
- "+"修饰符,为映射类型添加?修饰符或者是readonly修饰符
- "-"修饰符,为映射类型移除?修饰符或者是readonly修饰符
{-readonly [p in keyof T]-?: T[p]}
{+readonly [p in keyof T]+?: T[p]}
type Required<T> = { [P in keyof T]-?: T[p]}
type T = {
a?: string | undefined | null;
readonly b: number | undefined | null
}
// {
// a: string | null;
// readonly b: number | undefined | null
// }
"-"修饰符仅仅作用于带有?和readonly修饰符的属性,编译器在移除属性a的?修饰符时,会移除属性类型中的undefined类型,但是不会移除null类型。
条件类型
条件类型与条件表达式类似,语法为T extends U? X: Y
在该语法中,extends是关键字。
type T0 = true extends boolean ? string : number // string
type T1 = string extends boolean ? string : number // number
type TypeName<T> = T extends string
?'string'
: T extends number
? 'number'
: T extends boolean
? 'boolean'
: T extends undefined
? 'undefined'
: T extends Function
? 'function'
: 'object'
type T0 = TypeName<'a'> // string
type T1 = TypeName<0> // number
type T2 = TypeName<true> // boolean
type T3 = TypeName<undefined> // undefined
type T4 = TypeName<() => void> // function
type T5 = TypeName<string[]> // object
裸类型
裸类型是指裸露在外的没有任何装饰的类型参数,如果参数类型不是复合类型的组成部分而是独立出现,那么该类型参数称为裸类型参数
type T0<T> = T extends string ? true: false
type T1<T> = [T] extends [string] ? true: false
infer关键字
在extends语句中类型U的位置上允许使用infer关键字来定义可推断的类型变量,可推断的类型变量允许在条件类型的true分支中引用,就是类型x的位置上使用
T extends infer U ? U : Y
type CT<T> = T extends Array<infer U> ? U : never;
type T = CT<Array<number>> // 推断出来的变量U的实际类型为number类型
type CT<T> = T extends {a: infer M; b: infer N} ? [M, N] : never
type T = CT<{a: string; b: number}> // [string , number]