持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第15天,点击查看活动详情
映射类型
有的时候,一个类型需要基于另外一个类型,但是你又不想拷贝一份,这个时候可以考虑使用映射类型
interface Type {
name: string;
age: string;
}
// 映射类型 只能使用类型别名 不能使用接口
// 因为使用interface定义映射类型,key中的[xxx]会被识别为索引类型
type MapType<T> = {
// keyof T ->
// 1. T必须是一个对象类型
// 2. keyof会将对象的所有键取出并组成对应的联合类型
// valueof T -> type valueof<T> = T[keyof T]
// 1. TS中是没有valueof 操作符的
// 2. 可以结合keyof创建一个valueof 工具类型
// 3. 通过该工具类型 可以返回一个对象的所有值组成的联合类型
// T[key] -> 根据key 取出对应的 value 的对应类型
// key in 联合类型 -- key必须是联合类型中的某一种
[key in keyof T]: T[key]
}
type newType = MapType<Type>
/*
typeof newType
-> {
name: string;
age: string;
}
*/
映射修饰符
| 修饰符 | 说明 |
|---|---|
| readonly | 用于设置属性只读 |
| ? | 用于设置属性可选 |
interface Type {
name: string;
age: number;
}
type MapType<T> = {
readonly [prop in keyof T]?: T[prop]
}
type newType = MapType<Type>
/*
type newType = {
readonly name?: string | undefined;
readonly age?: number | undefined;
}
*/
你可以通过前缀 - 或者 + 删除或者添加这些修饰符,如果没有写前缀,相当于使用了 + 前缀
interface Type {
name?: string;
age?: number;
}
type MapType<T> = {
// +readonly 属性添加只读属性 默认就是+ 所以+ 可以省略
// -? 表示属性 去除只读属性 设置为必传属性
+readonly [prop in keyof T]-?: T[prop]
}
type newType = MapType<Type>
/*
=>
type newType = {
readonly name: string;
readonly age: number;
}
*/
条件类型
很多时候,日常开发中我们需要基于输入的值来决定输出的值,同样我们也需要基于输入的值的类型来决定输出的值的类型
条件类型(Conditional types)就是用来帮助我们描述输入类型和输出类型之间的关系
SomeType extends OtherType ? TrueType : FalseType
我们可以将之前的函数重载使用条件类型进行重写
// 函数重载
function sum(num1: number, num2: number): number
function sum(num1: string, num2: string): string
function sum(num1: any, num2: any) {
return num1 + num2
}
// 条件类型
// num1 和 num2 的类型 都是T 确保了 num1 和 num2的类型必须一致
// T extends number | string 确保了传入的T的类型不是string就是number
type Sum = <T extends number | string>(num1: T, num2: T) => T extends number ? number : string
const sum: Sum = (num1: any, num2: any) => num1 + num2
// 当我们不主动传入泛型的值的时候,tsc会自动推导T的值
console.log(sum(1, 2))
console.log(sum('1', '2'))
类型推断 - infer
条件类型提供了 infer 关键字可以从正在比较的类型中推断类型,然后在 true 分支里引用该推断结果
// 传入一个函数,推导出函数的返回值类型
// T extends (...args: any[]) => any --- 确保传入的泛型是函数类型
// T extends (...args: any[]) => infer R
// --- infer R 表示根据传入的泛型值推导类型并存入变量R中
// --- 也就是说 R是自己定义的变量 -- 约定为 一个大写的字母
type MyReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never
// 传入一个喊,推导出函数的参数类型
type MyParamerType<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never
function foo(arg1: string, arg2: number) {
console.log('foo')
}
type returnType = MyReturnType<typeof foo> // typeof returnType -> void
type paramerType = MyParamerType<typeof foo> // typeof paramerType -> [arg1: string, arg2: number]
分发条件类型
当在泛型中使用条件类型的时候,如果传入一个联合类型,就会变成 分发的(distributive)
type toArray<T> = T[]
type arr1 = toArray<number> // typeof arr1 -> number[]
type arr2 = toArray<number | string> // typeof arr2 -> (number | string)[]
type toArray<T> = T extends any ? T[] : never
type arr1 = toArray<number> // typeof arr1 -> number[]
// toArray内部使用了条件类型 所以 toArray<number | string> === toArray<number> | toArray<string>
type arr2 = toArray<number | string> // typeof arr2 -> number[] | string[]
as
type obj = {
name: string;
age: number;
}
type MyOmit<T, K> = {
// 在条件类型中,keyof关键字后边也可以加速as关键字
// 此时会将符合条件的联合类型 交给as后的语句进行执行
// P in keyof T as P extends K ? never : P
// -> 即需要满足P in keyof T 又需要满足 P extends K ? never : P
[P in keyof T as P extends K ? never : P]: T[P]
}
type foo = MyOmit<obj, 'name'>