如何在TypeScript中添加数组类型注解同时提取对象属性联合类型?
如何为数组添加类型注解同时保留属性的联合类型推导?
我尝试从对象数组的属性中推导联合类型:
const fontLib = [ { name: 'foo', subsets: ['latin'], }, { name: 'bar', subsets: ['latin'], }, ] as const; type FontName = (typeof fontLib)[number]['name'];
此时FontName类型会如预期般为'foo'|'bar'。
但当我为fontLib添加类型注解后,推断出的类型会被注解类型覆盖:
const fontLib: FontLib[] = [ { name: 'foo', subsets: ['latin'], }, { name: 'bar', subsets: ['latin'], }, ] as const; interface FontLib<T extends string = string> { name: T subsets: string[]; } type FontName = (typeof fontLib)[number]['name']; // 此时FontName类型为string
请问如何添加类型注解且不影响联合类型的推导?
解决方案
1. 使用泛型函数约束类型并保留字面量
通过泛型函数让TypeScript自动推导数组元素的具体字面量类型,同时约束元素符合FontLib接口:
interface FontLib<T extends string = string> { name: T; subsets: string[]; } const createFontLib = <T extends string>(fonts: readonly FontLib<T>[]) => fonts; const fontLib = createFontLib([ { name: 'foo', subsets: ['latin'], }, { name: 'bar', subsets: ['latin'], }, ] as const); type FontName = (typeof fontLib)[number]['name']; // 'foo' | 'bar'
2. 使用satisfies关键字(TypeScript 4.9+)
satisfies关键字可以确保值符合指定类型约束,但不会覆盖原本的字面量类型推导,是最简洁的方案:
interface FontLib<T extends string = string> { name: T; subsets: string[]; } const fontLib = [ { name: 'foo', subsets: ['latin'], }, { name: 'bar', subsets: ['latin'], }, ] as const satisfies readonly FontLib[]; type FontName = (typeof fontLib)[number]['name']; // 'foo' | 'bar'
3. 显式指定泛型参数(手动指定联合类型)
如果不想用函数或satisfies,可以显式给FontLib传入预先定义的联合类型,但这种方式需要手动维护联合类型,灵活性较低:
type FontNames = 'foo' | 'bar'; interface FontLib<T extends string = string> { name: T; subsets: string[]; } const fontLib: FontLib<FontNames>[] = [ { name: 'foo', subsets: ['latin'], }, { name: 'bar', subsets: ['latin'], }, ] as const; type FontName = (typeof fontLib)[number]['name']; // 'foo' | 'bar'
原因说明
直接给数组添加FontLib[]注解时,TypeScript会将数组元素的类型宽化为FontLib<string>(因为FontLib的默认泛型参数是string),导致name属性的字面量类型被覆盖为string。而泛型函数或satisfies可以在约束类型的同时,保留数组元素的具体字面量类型信息,从而让联合类型推导正常工作。
内容的提问来源于stack exchange,提问作者cliff yang
相关产品推荐
相关产品推荐

