You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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';
}

关键说明

  1. 泛型约束:T extends { id?: number | null }确保传入的类型一定包含id字段(可选或必选),避免类型歧义。
  2. 类型精准判断: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 04:40:28