携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
困难
Union to Intersection(联合转交叉)
type UnionToIntersection<U> =
(U extends any ? (arg: U) => any : never) extends (arg: infer I) => void
? I
: never
// 结果:'foo' & 42 & true
type I = Union2Intersection<'foo' | 42 | true>
答案参考自解答区👍比较多的
当使用infer推断会发生逆变的类型中时,则推断结果会得到一个交叉类型:
type Bar<T> =
T extends { a: (x: infer U) => void; b: (x: infer U) => void }
? U
: never;
// string
type T1 = Bar<{ a: (x: string) => void; b: (x: string) => void }>;
// string & number
type T2 = Bar<{ a: (x: string) => void; b: (x: number) => void }>;
在官方文档中有infer对应的说明。
所以代入到上面的解答与示例中可以这么去看:
注意:联合类型作用于泛型时会发生分布行为
type UnionToIntersection<'foo' | 42 | true> =
((arg: 'foo') => any | (arg: 42) => any | (arg: true) => any) extends (inferArg: infer I) => void
? I
: never
也就符合了上面说的infer推断情况,因为inferArg的类型存在逆变,所以该类型必须是'foo' & 42 & true,这样才能满足。但是怎么可能有类型是'foo' & 42 & true呢,所以最后会得到never。
Get Required(保留所有必填字段)
type GetRequired<T> = {
[P in keyof T as T[P] extends Required<T>[P] ? P : never]: T[P]
}
// 使用示例:expected to be { foo: undefined }
type I = GetRequired<{ foo: undefined; bar?: undefined }>
答案参考自解答区
上面用到了工具类型Required,源码如下:
type Required<T> = { [P in keyof T]-?: T[P]; }
首先使用到了 as 进行键的重新映射,然后通过Required将对象的属性变为必选的,也就是通过-?来进行处理,这样TS就能准确地识别了。
可选属性的值可不是单单多了个undefined类型而已,要不然上面的示例都没法通过了。
Get Optional(保留所有可选字段)
type GetOptional<T> = {
[P in keyof T as T[P] extends Required<T>[P] ? never : P]: T[P]
}
// 使用示例:expected to be { bar?: string }
type I = GetOptional<{ foo: number, bar?: string }>
一开始我还想用Partial来进行处理:
type GetOptional<T> = {
[P in keyof T as T[P] extends Partial<T>[P] ? P : never]: T[P]
}
发现行不通,所以还是得用Required来进行处理。
其实就是跟上面的Get Required的处理相反,只是这次用来排除掉必选字段而已。
所以处理步骤就是:
- 使用 as 键的重新映射
- 判断当前键对应的值类型是不是必选字段
- 是则返回
never - 否则保留当前键
- 是则返回