如何在含联合类型与泛型的数组中正确查找元素并类型转换?
问题
现有如下TypeScript类型声明(不可修改):
types.ts
type ItemKind = 'A' | 'B'; type ValidItem<TItemKind extends ItemKind = ItemKind> = { readonly type: TItemKind; readonly id: number; }; type EmptyItem<TItemKind extends ItemKind = ItemKind> = { readonly type: `empty-${TItemKind}`; }; export type Item = ValidItem | EmptyItem;
在index.ts中,通过id查找Item数组元素后,无法直接访问id属性:
index.ts
import { type Item } from './types'; const items: Item[] = [ {type: 'A', id: 42}, {type: 'empty-A'}, {type: 'empty-B'} ]; console.debug('Items', items); const targetId = 42; const item = items.find((item) => item.type === 'A' && item.id === targetId // 查找逻辑正常,但类型未自动缩小 ); if (item) { console.debug('Item', item); // console.debug('Id', item.id); // 此行编译报错,TypeScript无法确定item包含id属性 }
运行代码时能正确打印匹配元素,但TypeScript编译报错,因为无法自动推断item是包含id的ValidItem类型。
解决方案
方法1:自定义类型守卫(类型安全,推荐)
定义类型谓词函数,明确告知TypeScript如何判断元素是否为指定类型的ValidItem:
import { type Item, type ValidItem, type ItemKind } from './types'; // 类型守卫:判断item是否为指定类型的ValidItem function isValidItemOfType<T extends ItemKind>(item: Item, kind: T): item is ValidItem<T> { return item.type === kind && 'id' in item; } // 使用类型守卫执行查找 const item = items.find(item => isValidItemOfType(item, 'A') && item.id === targetId); if (item) { console.debug('Id', item.id); // TypeScript已确定item为ValidItem<'A'>,可安全访问id }
方法2:用in操作符缩小类型
直接在if判断中检查id属性是否存在,TypeScript会自动缩小元素类型:
if (item && 'id' in item) { console.debug('Id', item.id); // 类型自动缩小为ValidItem,可访问id }
方法3:类型断言(快捷但需注意风险)
若能确保查找逻辑绝对正确,可直接用类型断言强制转换类型:
if (item) { console.debug('Id', (item as ValidItem).id); }
注意:类型断言不做运行时检查,若查找逻辑出错可能导致运行时异常,仅在逻辑确定无误时使用。
原理说明
TypeScript无法自动通过item.type === 'A'推断元素为ValidItem<'A'>,因为Item联合类型的两个分支(ValidItem和EmptyItem)的type字段规则需要手动关联。通过类型守卫或in操作符,我们可以明确告知TypeScript当前元素的具体类型,从而安全访问id属性。
内容的提问来源于stack exchange,提问作者Tristian
相关产品推荐
相关产品推荐

