TypeScript对象转单属性联合类型:如何限制额外属性报错?
问题:限制联合类型返回值仅包含指定属性
我需要将TypeScript对象的每个属性转换为独立对象的联合类型,例如:
{ 200: number; 500: string; }
转换为:
{ 200: number; 500?: undefined; } | { 500: string; 200?: undefined; }
目前已实现上述转换,但返回结果中包含额外属性时不会触发错误——比如返回{100: "why no error", 200: 100}时无报错,需要修改代码让这种情况触发错误。
原代码如下:
type AllKeys<T> = T extends unknown ? keyof T : never; type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never; type _ExclusifyUnion<T, K extends PropertyKey> = T extends unknown ? Id<T & Partial<Record<Exclude<K, keyof T>, never>>> : never; type ExclusifyUnion<T> = _ExclusifyUnion<T, AllKeys<T>>; type split_return_type<T> = { [K in keyof T]: { [P in K]: T[P] }; }; type ValueOf<T> = T[keyof T]; export type SchemezLayoutReturnType<T> = Promise< ExclusifyUnion<ValueOf<split_return_type<Required<T>>>> >; function TestFnx<T>(input: T, handleAsync: () => SchemezLayoutReturnType<T>) { return { input, handleAsync, } } type required_return_type = { 200: number; 500: string; } const rrr: required_return_type = { 200: 100, 500: "00" }; TestFnx(rrr, async () => { return { 100: "200", 200: 100, // 500: "oii", } });
解决方案
要禁止额外属性,需在联合类型的每个成员中明确禁止所有未指定的键,修改核心类型定义即可实现:
修改后的完整代码
type AllKeys<T> = T extends unknown ? keyof T : never; type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never; // 替换原_ExclusifyUnion,添加对额外键的严格限制 type _ExclusifyUnion<T, K extends PropertyKey> = T extends unknown ? Id< T & { // 限制原始类型中存在但当前成员没有的键只能为undefined [P in Exclude<K, keyof T>]?: never; } & { // 严格禁止所有不在原始类型键集合中的额外属性 [Q in Exclude<PropertyKey, K>]?: never; } > : never; type ExclusifyUnion<T> = _ExclusifyUnion<T, AllKeys<T>>; type split_return_type<T> = { [K in keyof T]: { [P in K]: T[P] }; }; type ValueOf<T> = T[keyof T]; export type SchemezLayoutReturnType<T> = Promise< ExclusifyUnion<ValueOf<split_return_type<Required<T>>>> >; function TestFnx<T>(input: T, handleAsync: () => SchemezLayoutReturnType<T>) { return { input, handleAsync, } } type required_return_type = { 200: number; 500: string; } const rrr: required_return_type = { 200: 100, 500: "00" }; TestFnx(rrr, async () => { return { 100: "200", // 此处触发错误:类型“string”不能赋值给类型“undefined” 200: 100, // 500: "oii", } });
实现原理
- 原类型仅处理了原始对象中存在但当前联合成员缺失的键,未限制完全不在原始对象中的额外键。
- 添加
[Q in Exclude<PropertyKey, K>]?: never;后,所有不在原始对象键集合K中的属性都会被标记为只能赋值undefined,若给这些键赋值其他类型则直接报错。 - 使用
?: never而非never,是因为联合类型的每个成员仅需保留自身的必填键,其他键允许不存在,但禁止赋予非undefined的值。
内容的提问来源于stack exchange,提问作者SILENT
相关产品推荐
相关产品推荐

