TypeScript如何改写TypeOfShape使形状推导的可选必填属性合并而非交叉
解决方案
你可以通过一个类型扁平化工具泛型包裹原有计算结果,或者直接重构TypeOfShape的映射逻辑,实现单个对象类型的展示效果:
最简修改方案
仅需新增一个Simplify工具泛型,外层包裹原有逻辑即可,不会修改原有功能的兼容性:
// 新增类型扁平化工具,作用是合并交叉类型为单个对象 type Simplify<T> = { [K in keyof T]: T[K] } & {} class Type<T> { // just a marker declare readonly _type: T; } type AnyType = Type<any>; class Optional<T extends AnyType> { constructor(readonly inner: T) {} } type Shape = { [k: string]: AnyType | Optional<AnyType> }; // 原有逻辑不变,外层套Simplify即可 type TypeOfShape<T extends Shape> = Simplify< & { [K in keyof T as T[K] extends AnyType ? K : never]: T[K] extends AnyType ? T[K]["_type"] : never } & { [K in keyof T as T[K] extends Optional<AnyType> ? K : never]?: T[K] extends Optional<AnyType> ? T[K]["inner"]["_type"] : never } >; const foo = {bar: new Type<number>(), baz: new Optional(new Type<string>())} as const; // 推导结果为 { readonly bar: number; readonly baz?: string } type Foo = TypeOfShape<typeof foo>; const foo2 = {bar: new Type<number>()} as const; // 推导结果为 { readonly bar: number } type Foo2 = TypeOfShape<typeof foo2>
原理说明
Simplify泛型的作用是触发TS的类型即时计算,将分散的交叉类型片段合并为单一对象结构展示,同时完全保留原有类型的属性规则和兼容逻辑,和原有实现的功能完全一致。
如果不想额外引入独立的工具泛型,也可以直接将合并逻辑内嵌到TypeOfShape中,效果完全相同:
type TypeOfShape<T extends Shape> = ({ [K in keyof T as T[K] extends AnyType ? K : never]: T[K]['_type'] } & { [K in keyof T as T[K] extends Optional<AnyType> ? K : never]?: T[K] extends Optional<AnyType> ? T[K]['inner']['_type'] : never }) extends infer R ? { [K in keyof R]: R[K] } : never
内容的提问来源于stack exchange,提问作者user3612643
相关产品推荐
相关产品推荐

