如何从已声明为Interface的对象键创建TypeScript新类型?
解决方案
问题出在你给allCodes显式标注了List类型,而List的索引签名是[key: string],这会让TypeScript丢失具体的键名信息,把allCodes的类型宽泛化为「任意字符串键对应ListField」,所以keyof typeof allCodes得到的是string | number而非你需要的具体键名联合类型。
下面是几种可行的实现方式:
方式一:去掉显式类型标注
直接让TypeScript自动推断allCodes的具体类型,这样就能保留所有键名信息:
interface ListField { code: number; message: string; } export const allCodes = { FIRST: { code: 1, message: 'message 1', }, SECOND: { code: 2, message: 'message 2', }, THIRD: { code: 3, message: 'message 3', }, }; export type ListType = keyof typeof allCodes; // 类型为 "FIRST" | "SECOND" | "THIRD"
方式二:保留类型约束的同时推导键名
如果需要确保allCodes的结构符合ListField的要求,可以用泛型函数来创建它,既约束值的类型,又保留具体键名:
interface ListField { code: number; message: string; } function createCodes<T extends Record<string, ListField>>(codes: T) { return codes; } export const allCodes = createCodes({ FIRST: { code: 1, message: 'message 1', }, SECOND: { code: 2, message: 'message 2', }, THIRD: { code: 3, message: 'message 3', }, }); export type ListType = keyof typeof allCodes; // 得到目标联合类型
方式三:使用const断言+satisfies(TypeScript 4.9+)
这种方式既可以用const断言锁定键名和值的类型,又通过satisfies确保结构符合ListField的约束:
interface ListField { code: number; message: string; } export const allCodes = { FIRST: { code: 1, message: 'message 1', }, SECOND: { code: 2, message: 'message 2', }, THIRD: { code: 3, message: 'message 3', }, } as const satisfies Record<string, ListField>; export type ListType = keyof typeof allCodes;
内容的提问来源于stack exchange,提问作者crivella
相关产品推荐
相关产品推荐

