TypeScript中能否编写条件映射类型?联合类型参数报错求助
问题:泛型条件类型处理联合键时的类型错误
想要编写一个泛型类型,支持可选参数指定基于另一相似类型重映射的键。现有Mapped类型单独使用时正常工作,但添加条件判断后的ConditionalMapped类型,在传入'a' | 'b'这类联合类型参数时会报错(提示b不是字符串),希望找到无需拆分类型的合并解决方法。
原始可正常工作的Mapped类型
interface Origin { a: string; b: string; } interface SimiliarType { a: boolean; b: boolean; } type Mapped<K extends keyof Origin> = Omit<Origin, K> & { [Property in K]: SimiliarType[Property]; }; const mapped: Mapped<'b'> = { a: '', b: false }; // OK const mappedUnion: Mapped<'a' | 'b'> = { a: false, b: false }; // OK
添加条件后的报错代码
type ConditionalMapped<K extends keyof Origin | undefined = undefined> = K extends keyof Origin ? Mapped<K> : Origin; const conditionalMapped1: ConditionalMapped = { a: '', b: '' }; // OK const conditionalMapped2: ConditionalMapped<'b'> = { a: '', b: false }; // OK const conditionalMapped3: ConditionalMapped<'a' | 'b'> = { a: false, b: false }; // ERROR: b is not a string
原因分析
这是TypeScript分布式条件类型的特性导致的:当条件类型的泛型参数是联合类型时,TypeScript会自动将联合类型的每个成员单独代入条件判断,最后把结果合并成新的联合类型。
对于ConditionalMapped<'a' | 'b'>,会被展开为Mapped<'a'> | Mapped<'b'>:
Mapped<'a'>的类型是Omit<Origin, 'a'> & {a: boolean},即{a: boolean, b: string}Mapped<'b'>的类型是Omit<Origin, 'b'> & {b: boolean},即{a: string, b: boolean}
最终的联合类型要求值符合其中任意一个类型,但{a: false, b: false}既不符合{a: boolean, b: string}(b不是string),也不符合{a: string, b: boolean}(a不是string),因此报错。
解决方案
要避免分布式条件类型的展开,只需将泛型参数包裹在一个元组(或其他非联合结构)中,让TypeScript不再触发分布式展开逻辑:
type ConditionalMapped<K extends keyof Origin | undefined = undefined> = [K] extends [keyof Origin] ? Mapped<K> : Origin;
修改后,当传入联合类型'a' | 'b'时,[K]是['a' | 'b'],与[keyof Origin](即['a' | 'b'])匹配,直接返回Mapped<'a' | 'b'>,而非展开为联合类型。此时三个变量均可正常通过类型检查:
const conditionalMapped1: ConditionalMapped = { a: '', b: '' }; // OK const conditionalMapped2: ConditionalMapped<'b'> = { a: '', b: false }; // OK const conditionalMapped3: ConditionalMapped<'a' | 'b'> = { a: false, b: false }; // OK
内容的提问来源于stack exchange,提问作者David Jensen
相关产品推荐
相关产品推荐

