常见泛型变量代表
- T(Type):在定义泛型时通常用作第一个类型变量名称,但实际上 T 可以用任何有效名称代替
- K(Key):表示对象中的键类型;
- V(Value):表示对象中的值类型;
- E(Element):表示元素类型。
<>中的类型是传递类型,比如
写法举例:
- 泛型接口
interface Teacher<T> {
(arg: T): T; //ok
[key: T]: T; //error
}
可见泛型不能引用在「索引签名」
- 泛型函数
function introduce<T,U>(name:T,age:U): T {
return name
}
console.log(introduce<string, number>("hannie",18));
注:返回值类型和参数类型必须在传递类型中找到,此处返回值类型是T,返回值类型是T和U,传递类型是<T,U>
- 泛型类
class Teacher<T> {
age: T;
add: (x: T, y: T) => T;
}
let teacher = new Teacher<number>();
teacher.age = 0;
teacher.add = function (x, y) {
return x + y;
};
下面细讲泛型工具类型
typeof:推算变量类型
- 推算字符串遍历类型
let str1 = 'fooooo'
type Type1 = typeof str1 // type string
- 推算接口类型
interface Person {
name: string;
age: number;
}
const sem: Person = { name: 'semlinker', age: 30 };
type Sem= typeof sem; // -> Person
function toArray(x: number): Array<number> {
return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]
keyof:获取对象的 key 值集合
- interface对象key
interface Person {
name: string;
age: number;
}
type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join"
type K3 = keyof { [x: string]: Person }; // string | number
- type对象key
type Teacher = {
id: string;
name: string;
};
type TeacherKeys = keyof Teacher; //"id" | "name"
let a: TeacherKeys = 'id' || 'name' // ok
let a: TeacherKeys = 'other' //error
这样就轻松获取到了对象 key 值的联合类型:"foo" | "bar"。
它也可以用在遍历中:
type Obj = {
foo: number;
bar: string;
}
type Copy = {
[K in keyof Obj]: Obj[K]
}
Copy 得到和 Obj 一模一样的类型
extends:三目运算和继承
- 在 interface 中表示类型扩展
// 表示类型扩展
interface A {
a: string
}
interface B extends A { // { a: string, b: string }
b: string
}
- 在条件类型语句中表示布尔运算
type User<T> = 'T extends'
type u = User<boolean> //<>中任何类型都行
let a: u = 'T extends' //User一样的值
type User<T> = T extends string ? string : never
type A = User<number> // never
type B = User<string> // string
type C = User<'hhy'> // string
let b:B = '213'
type User<T> = T extends string ? 'string' : never
type A = User<number> // never
type B = User<string> // string
type C = User<'hhy'> // string
let b:B = 'string' //不能是其他
- 在泛型中起到限制的作用
type Foo<T extends object> = T
type A = Foo<number> // 类型“number”不满足约束“object”。
type B = Foo<string> // 类型“string”不满足约束“object”。
type C = Foo<{}> // OK
- 在 class 中表示继承
class A {}
class B extends A {}
注:使 A extends B 在布尔运算或泛型限制中成立的条件是 A 是 B 的子集,也就是 A 需要比 B 更具体,至少是跟 B 一样。
type K = '1' extends '1' | '2' ? 'true' : 'false' // "true"
type L = '1' | '2' extends '1' ? 'true' : 'false' // "false"
type M = { a: 1 } extends { a: 1, b: 1 } ? 'true' : 'false' // "false"
type N = { a: 1, b: 1 } extends { a: 1 } ? 'true' : 'false' // "true"
infer:声明一个类型变量,也可以理解为在条件类型中给了它一个占位符,动态推导泛型
什么场景下还需要动态推导?通常是需要通过传入的泛型参数去获取新的类型,这和直接定义一个新的泛型参数不一样。
注:infer 一定是出现在条件类型中的。
type Get<T> = T extends infer R ? R: never
infer R 的位置代表了一个未知的类型,可以理解为在条件类型中给了它一个占位符,然后就可以在后面的三元运算符中使用它。
type T = Get<number>
// 经过计算
type Get<number> = number extends infer number ? number: never
// 得到
> number
它的使用非常灵活,它也可以出现在泛型位置
type Unpack<T> = T extends Array<infer R> ? R : T
type NumArr = Array<number>
type U = Unpack<NumArr>
// 经过计算
type Unpack<Array<number>> = Array<number> extends Array<infer R> ? R : T
// 得到
> number
in:在类型中表示类型映射,和索引签名的写法有些相似
遍历枚举类型
type Keys = "a" | "b" | "c"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any, c: any }
type Props = {
[key in 'count' | 'id']: number
}
const props1: Props = { // OK
count: 1,
id: 1
}
const props2: Props = {
count: '1', // ERROR
id: 1
}
ReturnType:获取方法的返回值类型
type A = (a: number) => string
type B = ReturnType<A> // string
Parameters:获取方法的参数类型
type EventListenerParamsType = Parameters<typeof window.addEventListener>;
// [type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions | undefined]
Exclude:计算在 T 中而不在 U 中的类型
type A = number | string
type B = string
type C = Exclude<A, B> // number
Extract:计算 T 中可以赋值给 U 的类型
type A = number | string
type B = string
type C = Extract<A, B> // string
NonNullable:从类型中排除 null 和 undefined
type A = {
a?: number | null
}
type B = NonNullable(A['a']) // number
Record:定义键类型为 Keys、值类型为 Values 的对象类型
enum ErrorCodes {
MethodError = 405,
ServerError = 500,
}
const ErrorMessageMap: Record<ErrorCodes, string> = {
[ErrorCodes.MethodError]: '方法错误',
[ErrorCodes.ServerError]: '服务器错误'
}
Partial: 将类型定义的属性变成可选
type Teacher = {
id?: string,
gender: 'male' | 'female'
}
type PartialTeacher = Partial<Teacher> // { id?: string, gender?: 'male' | 'female'}
function createTeacher (teacher: PartialTeacher = { gender: 'male' }) {}
Required:将对象类型的属性都变成必须,与Partial 的作用相反
type Teacher = {
id?: string,
gender: 'male' | 'female'
}
type RequiredTeacher = Required<Teacher>
function showInfo (user: RequiredTeacher) {
console.log(user.id) // 1
console.log(user.gender) //male
}
showInfo({id:'1',gender:'male'})
Readonly:将对象类型的属性都变成只读
type Teacher = {
id?: string,
gender: 'male' | 'female'
}
type ReadonlyTeacher = Readonly<Teacher>
const user: ReadonlyTeacher = {
id: '1',
gender: 'male'
}
user.gender = 'femail' // error ,因为它是只读属性。
Pick:挑选类型中的部分属性
type Person = {
age: number
name: string
city: string
address: string
province: string
}
type partInfo = Pick<Person, 'city' | 'address'>
const region: partInfo = {
city: 'beijingg',
address: 'chaoyang'
}
Omit :结合了 Pick 和 Exclude,将忽略对象类型中的部分 keys
type Person = {
age: number
name: string
city: string
address: string
province: string
}
type partInfo = Omit<Person, 'city' | 'address'>
const region: partInfo = {
age: 18,
name: 'hannie',
province: 'beijing'
} //除了Omit中的,必须写全其他,不然会报错