如何确保TypeScript接口包含枚举的所有值作为键?
解决TypeScript枚举全键映射的强制约束问题
假设我们有如下枚举:
enum Cars { Volvo = 'Volvo', Hyundai = 'Hyundai', Kia = 'Kia', Tesla = 'Tesla' }
需求是定义一个类型/接口,必须包含所有枚举值作为键,每个键的对应类型可以不同,新增枚举值时如果没补全映射就报错。之前尝试继承Record<Cars, Factory>无法触发缺失属性的错误,下面给出两种可行方案:
方案一:使用satisfies关键字(TypeScript 4.9+ 推荐)
先定义基础的映射类型,然后用satisfies关键字约束对象必须匹配Record<Cars, Factory>,同时保留每个键的具体类型:
// 先定义各个工厂类型示例 type SwedishFactoryType = { location: 'Sweden' }; type KoreanFactoryType = { location: 'Korea' }; type AmericanFactoryType = { location: 'USA' }; type Factory = SwedishFactoryType | KoreanFactoryType | AmericanFactoryType; // 定义映射对象并约束 const carToFactory = { [Cars.Volvo]: { location: 'Sweden' } as SwedishFactoryType, [Cars.Hyundai]: { location: 'Korea' } as KoreanFactoryType, [Cars.Kia]: { location: 'Korea' } as KoreanFactoryType, // 注释掉Tesla的话会直接报错:属性 "Tesla" 缺失 [Cars.Tesla]: { location: 'USA' } as AmericanFactoryType } satisfies Record<Cars, Factory>; // 提取类型供CarPassport使用 type CarToFactory = typeof carToFactory;
这样后续新增Cars枚举值时,只要没在carToFactory里补全对应键,TypeScript就会直接报错。同时CarToFactory类型会保留每个键的具体工厂类型,而非统一的Factory联合类型。
方案二:利用类型约束强制完全匹配
如果项目使用的TypeScript版本低于4.9,可以通过辅助类型检查是否存在缺失键:
type SwedishFactoryType = { location: 'Sweden' }; type KoreanFactoryType = { location: 'Korea' }; type AmericanFactoryType = { location: 'USA' }; type Factory = SwedishFactoryType | KoreanFactoryType | AmericanFactoryType; // 定义映射接口 interface CarToFactory { [Cars.Volvo]: SwedishFactoryType; [Cars.Hyundai]: KoreanFactoryType; [Cars.Kia]: KoreanFactoryType; // 缺失Tesla的话,下面的类型检查会报错 [Cars.Tesla]: AmericanFactoryType; } // 辅助类型:检查CarToFactory是否包含所有Cars键,缺失则返回never触发错误 type EnsureAllCarsMapped<T extends Record<Cars, Factory>> = T; // 触发检查,缺失键时这里会报错 type CheckCarMapping = EnsureAllCarsMapped<CarToFactory>;
当CarToFactory缺失某个枚举键时,EnsureAllCarsMapped<CarToFactory>会因为CarToFactory不满足Record<Cars, Factory>而报错,强制开发者补全映射。
配合泛型接口CarPassport使用
两种方案生成的CarToFactory类型都可以直接用于你的泛型接口:
interface CarPassport<TCar extends Cars> { company: TCar; manufacturer: CarToFactory[TCar]; year: number; } // 示例使用 const volvoPassport: CarPassport<Cars.Volvo> = { company: Cars.Volvo, manufacturer: { location: 'Sweden' }, year: 2024 };
这样manufacturer的类型会根据TCar自动推导为对应的工厂类型,类型安全得到保障。
内容的提问来源于stack exchange,提问作者Artem Yankin
相关产品推荐
相关产品推荐

