持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情
如何使用
<LookUp>类型能够根据某个属性在联合类型中查找对应的类型。
例如:我们想通过在联合类型Cat | Dog中搜索公共type字段来获取相应的类型。换句话说,在以下示例中,我们期望LookUp<Dog | Cat, 'dog'>获得Dog,LookUp<Dog | Cat, 'cat'>获得Cat。
interface Cat {
type: 'cat'
breeds: 'Abyssinian' | 'Shorthair' | 'Curl' | 'Bengal'
}
interface Dog {
type: 'dog'
breeds: 'Hound' | 'Brittany' | 'Bulldog' | 'Boxer'
color: 'brown' | 'white' | 'black'
}
type MyDog = LookUp<Cat | Dog, 'dog'> // expected to be `Dog`
如何实现
主要实现思路还是利用extends分布式条件类型进行判断。
type LookUp<U, T extends keyof U> = U extends {type: T} ? U : never
- 用
T extends keyof U进行类型约束,简单的说就是缩小T的取值范围,让错误在更早的阶段暴露出来 - 利用
extends分布式条件类型进行判断:U是否符合形如{type: T}的数据结构
再谈 extends 条件类型的用法
这里extends的类型约束以及条件类型的用法都在extends关键字中提到过, 类型约束的用法大家应该都很熟悉了,这里再将条件类型的一些注意点复习一下:
条件类型的常见表现形式为:
T extends U ? 'Y' : 'N'
要注意:
extends在条件类型中的作用和类型约束中的作用不一样- 条件类型只支持在
type中使用 extends作为条件类型时也是可以嵌套的,就像if语句一样。
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<string>; // "string"
type T1 = TypeName<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"
关于分配条件类型要注意:
1. extends关键字左侧的是一个泛型,且传入泛型的必须是联合类型,其他类型如交叉类型是没有分配效果的。
如果左侧不是泛型,直接传入一个联合类型,是没有分配效果的,只是一个简单的条件判断。
type A1 = 'x' extends 'x' ? string : number; // string
type A2 = 'x' | 'y' extends 'x' ? string : number; // number
// 如果分配生效的话,结果应该是 string | number
2. 分配操作只有在检查的类型是naked type parameter时才生效。
那么是什么是naked type parameter呢?直接翻译过来怪怪的,参数是裸的? 我的理解是没有对传进来的泛型参数进行一些额外操作,那么就符合naked type parameter的要求。
看一下以下的例子,更容易理解。这也是stackoverflow上一个高赞回答的例子:
type NakedUsage<T> = T extends boolean ? "YES" : "NO"
type WrappedUsage<T> = [T] extends [boolean] ? "YES" : "NO"; // wrapped in a tuple,
type Distributed = NakedUsage<number | boolean > // = NakedUsage<number> | NakedUsage<boolean> = "NO" | "YES"
type NotDistributed = WrappedUsage<number | boolean > // "NO"
type NotDistributed2 = WrappedUsage<boolean > // "YES"
其中,WrappedUsage对传入的泛型参数进行了操作,不属于naked type parameter,因此不会进行分配操作。