Appearance
泛型
泛型介绍
场景:参数可以是任意值,返回值将参数原样返回
输出类型和输入类型关联
两个关联的输入类型
常规写法
tsx
const identity = (arg) => arg;
type idBoolean = (arg: boolean) => boolean;
type idNumber = (arg: number) => number;
type idString = (arg: string) => string;
...js
identity("string").length; // ok
identity("string").toFixed(2); // ok
identity(null).toString(); // ok
...使用泛型重构代码,新建泛型变量 T,分别在参数和返回值中占位
tsx
function identity<T>(arg: T): T {
return arg
}T 是一个抽象类型,只有在调用时才确定值。
例子:定义多个变量
tsx
function identity<T, U>(value: T, message: U): T {
console.log(message)
return value
}
// 1.显示设定类型
console.log(identity<Number, string>(68, 'Semlinker'))
// 2.编译器自动推导类型
console.log(identity(68, 'Semlinker'))泛型约束
tsx
function trace<T>(arg: T): T {
console.log(arg.size) // Error: Property 'size doesn't exist on type 'T'
return arg
}问题:T 可以是任何类型,不管使用什么属性都会报错(除非这个属性和方法是所有集合共有)
思路:参数类型包含 size 属性即可,可通过 extends
tsx
interface Sizeable {
size: number
}
function trace<T extends Sizeable>(arg: T): T {
console.log(arg.size)
return arg
}泛型工具类型
typeof
typeof:获取变量或属性的类型
tsx
interface Person {
name: string
age: number
}
const sem: Person = { name: 'semlinker', age: 30 }
type Sem = typeof sem // type Sem = Person也可获取函数对象类型
tsx
function toArray(x: number): Array<number> {
return [x]
}
type Func = typeof toArray // -> (x: number) => number[]keyof
keyof:获取某种类型的所有键名,返回类型是联合类型
tsx
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 | numbertypescript 支持 两种索引签名,数字索引、字符串索引
为了同时支持 两种索引类型,数字索引的返回值,必须是字符串索引返回值的子类。
原因:使用数字索引,会先被 JavaScript 转换为 字符串索引进行操作。
tsx
interface StringArray {
// 字符串索引 -> keyof StringArray => string | number
[index: string]: string
}
interface StringArray1 {
// 数字索引 -> keyof StringArray1 => number
[index: number]: string
}keyof 作用
限制属性名的范围
tsx
function prop<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key]
}T extends object:T 约束该类型为 object 子类型
K extends keyof T:K 约束键名为 T 键名子类型
访问已存在的属性
js
type Todo = {
id: number;
text: string;
done: boolean;
}
const todo: Todo = {
id: 1,
text: "Learn TypeScript keyof",
done: false
}
function prop<T extends object, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const id = prop(todo, "id"); // const id: number
const text = prop(todo, "text"); // const text: string
const done = prop(todo, "done"); // const done: boolean注意:会阻止访问不存在的属性
js
const date = prop(todo, "date");
Argument of type '"date"' is not assignable to parameter of type '"id" | "text" | "done"'.in
in 用来遍历枚举类型
js
type Keys = "a" | "b" | "c"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any, c: any }infer
infer:条件判断中,类型推导
关键:看 infer 放置位置,在哪就推断哪个值
格式:使用 infer 声明一个变量类型,条件类型判断为 true 时生效
js
type ExtractSelf<T> = T extends (infer U) ? U : T;
type T1 = ExtractSelf<string>; // string
type T2 = ExtractSelf<() => void>; // () => void
type T3 = ExtractSelf<Date[]>; // Date[]
type T4 = ExtractSelf<{ a: string }>; // { a: string }ExtractArrayItemType:推断元素类型。普通元素直接返回类型,数组元素返回数组中元素类型
js
type ExtractArrayItemType<T> = T extends (infer U)[] ? U : T;
// 条件判断都为 false,返回 T
type T1 = ExtractArrayItemType<string>; // string
type T2 = ExtractArrayItemType<() => number>; // () => number
type T4 = ExtractArrayItemType<{ a: string }>; // { a: string }
// 条件判断为 true,返回 U
type T3 = ExtractArrayItemType<Date[]>; // Date(infer U)[] 可被分配为 Date[],U 所在的位置是 Date,返回 Date
根据优先级,此处 infer U 需加上 ()
ExtractReturnType:推断函数返回值
js
type ExtractReturnType<T> = T extends () => (infer U) ? U : T;
// 条件判断为 true,返回 U
type T1 = ExtractReturnType<() => number>; // number。ExtractAllType:推断出联合类型
js
type ExtractAllType<T> = T extends { x: infer U, y: infer U } ? U : T;
type T1 = ExtractAllType<{ x: string, y: number }>; // string | number
// 任意类型 推断
type ExtractAllType<T> = T extends { [k: string]: infer U } ? U : T;
type T1 = ExtractAllType<{ x: string, y: number, z: boolean }>; // string | number | booleanExtractArrayItemType:元组类型转为联合类型
js
type ExtractArrayItemType<T> = T extends (infer U)[] ? U : T;
type ItemTypes = ExtractArrayItemType<[string, number]>; // string | numberextends
extends 添加泛型约束
js
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}泛型函数被定义了约束。此时传入的值,必须要包含 length 属性
js
loggingIdentity(3) // Error, number doesn't have a .length property
loggingIdentity({ length: 10, value: 3 }) // ok场景:获取 T 对应 key 的类型
tsx
type MessageOf<T> = T['message']
// Type '"message"' cannot be used to index type 'T'.T 没有 message 属性,需要通过 extends 进行约束
tsx
type MessageOf<T extends { message: unknown }> = T['message']设置一个默认类型 never
tsx
type MessageOf<T> = T extends { message: unknown } ? T['message'] : never
interface Email {
message: string
}
interface Dog {
bark(): void
}
type EmailMessageContents = MessageOf<Email> // string
type DogMessageContents = MessageOf<Dog> // never场景:如果是数组,获取值类型,反之原样返回
tsx
type Flatten<T> = T extends any[] ? T[number] : T //
// Extracts out the element type.
type Str = Flatten<string[]> // string
// Leaves the type alone.
type Num = Flatten<number> // numberT[number]:获取数组值