如何让TypeScript的test函数在非数量型Item时无需传requirements参数
TypeScript 可选属性优化问题
问题背景
定义了如下类型和函数:
type Item = 'itemA' | 'itemB' | 'itemC' | 'itemD'; type QuantityBasedItem = 'itemA' | 'itemC'; // 数量型项示例定义 type BooleanBasedItem = Exclude<Item, QuantityBasedItem>; type MyType<T extends Item> = { name: T; requirements: T extends QuantityBasedItem ? { units: number } : undefined; }; const getQuantityBasedItemData = (item: QuantityBasedItem, units: number) => ({item, units}); const getBooleanBasedItemData = (item: BooleanBasedItem) => item; function test<T extends Item>(itemData: MyType<T>) { if(itemData.requirements?.units){ getQuantityBasedItemData(itemData.name, itemData.requirements) } else { getBooleanBasedItemData(itemData.name) } }
当前调用test时,非数量型项必须显式传入requirements: undefined才能通过类型检查:
test({name: 'itemB', requirements: undefined}); // 正常运行 test({name: 'itemB'}); // 类型报错
需要修改代码,让非数量型项调用test时无需传入requirements参数。
解决方案
方法一:拆分联合类型
直接将MyType拆分为数量型和布尔型两种场景的联合类型,明确区分必填和可选属性:
type Item = 'itemA' | 'itemB' | 'itemC' | 'itemD'; type QuantityBasedItem = 'itemA' | 'itemC'; type BooleanBasedItem = Exclude<Item, QuantityBasedItem>; // 联合类型分别定义两种项的结构 type MyType = | { name: QuantityBasedItem; requirements: { units: number } } | { name: BooleanBasedItem; requirements?: undefined }; const getQuantityBasedItemData = (item: QuantityBasedItem, units: number) => ({item, units}); const getBooleanBasedItemData = (item: BooleanBasedItem) => item; function test(itemData: MyType) { if (itemData.requirements && 'units' in itemData.requirements) { getQuantityBasedItemData(itemData.name, itemData.requirements) } else { getBooleanBasedItemData(itemData.name) } } // 测试用例 test({name: 'itemA', requirements: {units: 5}}); // 正常 test({name: 'itemB'}); // 无需传requirements,正常 test({name: 'itemC', requirements: {units: 10}}); // 正常 test({name: 'itemD'}); // 正常
方法二:条件类型结合可选属性
通过交叉类型,让布尔型项的requirements成为可选属性:
type Item = 'itemA' | 'itemB' | 'itemC' | 'itemD'; type QuantityBasedItem = 'itemA' | 'itemC'; type BooleanBasedItem = Exclude<Item, QuantityBasedItem>; // 用交叉类型实现条件可选 type MyType<T extends Item> = { name: T; } & (T extends QuantityBasedItem ? { requirements: { units: number } } : { requirements?: undefined }); const getQuantityBasedItemData = (item: QuantityBasedItem, units: number) => ({item, units}); const getBooleanBasedItemData = (item: BooleanBasedItem) => item; function test<T extends Item>(itemData: MyType<T>) { if (itemData.requirements?.units) { getQuantityBasedItemData(itemData.name, itemData.requirements) } else { getBooleanBasedItemData(itemData.name) } } // 测试用例 test({name: 'itemA', requirements: {units: 5}}); // 正常 test({name: 'itemB'}); // 无需传requirements,正常 test({name: 'itemC', requirements: {units: 10}}); // 正常 test({name: 'itemD'}); // 正常
核心逻辑
两种方法的核心都是将布尔型项的requirements设置为可选属性,TypeScript会自动允许调用时省略该属性。联合类型方案更直观,类型守卫逻辑清晰;条件类型方案保留了泛型结构,适合需要复用MyType的场景。
内容的提问来源于stack exchange,提问作者GO VEGAN
相关产品推荐
相关产品推荐

