创建新字面量类型时如何使用推断类型?
如何在约束对象类型的同时提取字面量联合类型?
问题场景
我定义了表示商店目录的StoreCatalog接口:
interface StoreCatalog { products: { name: string }[]; }
当给配置对象加上这个类型标注并使用as const时,尝试提取产品名称的字面量类型只能得到string,而非预期的"apples" | "bananas":
const groceryStoreConfig: StoreCatalog = { products: [{ name: "apples" }, { name: "bananas" }], } as const; // 实际得到类型:string export type GroceryStoreProductNames = (typeof groceryStoreConfig)["products"][number]["name"];
如果移除类型标注,虽然能得到正确的字面量联合类型,但对象可以随意添加不符合StoreCatalog的属性,不会触发类型检查:
// 无类型约束,额外属性不会报错 const groceryStoreConfig = { products: [ { name: "apples" }, { name: "bananas", additionalProperty: "additional property" }, ], } as const; // 得到预期类型:"apples" | "bananas" export type GroceryStoreProductNames = (typeof groceryStoreConfig)["products"][number]["name"];
解决方案
方案1:使用satisfies关键字(TypeScript 4.9+)
satisfies关键字可以确保对象符合指定类型,同时保留其字面量的窄化类型信息,完美解决这个矛盾:
interface StoreCatalog { products: { name: string }[]; } const groceryStoreConfig = { products: [{ name: "apples" }, { name: "bananas" }], } as const satisfies StoreCatalog; // 正确得到类型:"apples" | "bananas" export type GroceryStoreProductNames = (typeof groceryStoreConfig)["products"][number]["name"]; // 尝试添加额外属性会触发类型错误 const invalidConfig = { products: [{ name: "apples", extra: "oops" }], } as const satisfies StoreCatalog; // 错误提示:对象字面量只能指定已知属性,'extra' 不在类型 '{ name: string; }' 中
方案2:泛型函数约束(兼容低版本TS)
如果你的项目还未升级到TypeScript 4.9,可以用泛型函数来实现类型约束和字面量保留:
interface StoreCatalog { products: { name: string }[]; } // 泛型函数确保传入的配置符合StoreCatalog类型 function createStoreCatalog<T extends StoreCatalog>(config: T) { return config as const; } const groceryStoreConfig = createStoreCatalog({ products: [{ name: "apples" }, { name: "bananas" }], }); // 正确得到类型:"apples" | "bananas" export type GroceryStoreProductNames = (typeof groceryStoreConfig)["products"][number]["name"]; // 添加额外属性会触发类型错误 const invalidConfig = createStoreCatalog({ products: [{ name: "apples", extra: "oops" }], }); // 错误提示:类型 '{ name: string; extra: string; }' 不能赋值给类型 '{ name: string; }'
原因说明
直接给变量添加StoreCatalog类型标注时,TypeScript会将变量的类型拓宽到StoreCatalog,忽略as const带来的字面量窄化效果,因此products中的name会被视为宽泛的string类型。而satisfies或泛型函数的方式,既保证了对象结构符合StoreCatalog的要求,又保留了as const带来的字面量类型信息,从而能正确提取出具体的产品名称联合类型。
内容的提问来源于stack exchange,提问作者tpho
相关产品推荐
相关产品推荐

