如何在TypeScript中定义类型化函数过滤非空ID的对象?
过滤含有效ID的数据库实体
问题背景
我定义了一个可在UI端创建或从数据库返回的实体:
interface Task { id?: number | null, description: string, }
从数据库返回的实体必须包含有效的id(number类型),因此定义了如下类型来区分:
type DatabaseRecord<T extends {id: number | null | undefined}> = Omit<T, 'id'> & {id: number}
尝试编写类型守卫函数过滤出数据库返回的实体,但实现无法正常工作:
export function isInputFromDatabase<T>(input: null | undefined | T): input is DatabaseRecord<T> { return input !== null && input !== undefined && !!input.id; } // 使用场景 source$: Observable<Task | null> = store.currentTask$ source$.pipe(filter(isInputFromDatabase)).subscribe((task) => doSmthWithId(task.id))
需要实现一个能正确过滤出id不为null/undefined的实体的方法。
问题分析
当前的类型守卫存在两个核心问题:
- 泛型
T没有约束,TypeScript无法确认输入类型是否包含id字段,类型检查不严谨。 !!input.id仅能在运行时排除null/undefined,但无法让TypeScript将input.id的类型精准缩小到number——不仅会误判合法的0(如果数据库允许id为0),类型层面也无法区分number和其他非空值。
解决方案
调整类型守卫,给泛型加上明确约束,并通过typeof检查来精准判断id的类型:
type DatabaseRecord<T extends {id: number | null | undefined}> = Omit<T, 'id'> & {id: number}; export function isInputFromDatabase<T extends { id?: number | null }>( input: null | undefined | T ): input is DatabaseRecord<T> { // 同时判断输入非空,且id是number类型 return input !== null && input !== undefined && typeof input.id === 'number'; }
关键说明
- 泛型约束:
T extends { id?: number | null }确保传入的类型一定包含id字段(可选或必选),避免类型歧义。 - 类型精准判断:
typeof input.id === 'number'在运行时直接检查类型,同时让TypeScript能将input.id的类型缩小为number,完美匹配DatabaseRecord<T>的定义。
使用验证
现在在Observable管道中使用该函数,TypeScript会正确推断出过滤后的task类型为DatabaseRecord<Task>,task.id会被识别为number类型,不会出现类型报错:
source$: Observable<Task | null> = store.currentTask$; source$.pipe(filter(isInputFromDatabase)).subscribe((task) => { // task.id 类型为 number,可安全传入 doSmthWithId doSmthWithId(task.id); });
内容的提问来源于stack exchange,提问作者SkaaRJ
相关产品推荐
相关产品推荐

