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

如何实现排除对象类型中类实例属性的TypeScript工具类型?

TypeScript工具类型:排除对象中类实例属性(ORM列提取场景)

在ORM开发中,我们常需要从实体类型里提取纯列属性,自动跳过关联用的类实例属性。需求很明确:写一个通用TS工具类型,接收任意对象类型,返回剔除了类实例属性的新类型。

你的尝试问题分析

你当前的写法犯了一个核心错误:

type ExcludeClasses<T> = {
    [K in keyof T as T[K] extends { new(): any } ? never : K]: T[K]
}

这里的{ new(): any }匹配的是类的构造函数类型,而不是类的实例类型。比如A是实例类型,A extends { new(): any }会返回false,导致a、b属性根本不会被排除,完全达不到预期。

另外你提到的两个替代方案也有硬伤:

  • 用Omit手动列属性键:灵活性极差,新增关联属性就得同步修改,维护成本太高
  • 定义AllOrmTypes联合类型:很容易遗漏实体类,扩展性不足

可行解决方案

方案1:基于实体基类的精准匹配(推荐)

绝大多数ORM框架都会要求所有实体类继承一个统一基类(比如TypeORM的BaseEntity)。利用这一点可以精准识别并排除关联属性:

// 假设所有ORM实体都继承自这个基类
class BaseEntity {}

class A extends BaseEntity {}
class B extends BaseEntity {}

type MyType = {
    a: A,
    b: B,
    n: number,
    str: string,
    createdAt: Date
}

// 排除所有BaseEntity子类实例的属性
type ExcludeEntityProperties<T> = {
    [K in keyof T as T[K] extends BaseEntity ? never : K]: T[K]
}

type test = ExcludeEntityProperties<MyType>
// 最终类型:{ n: number; str: string; createdAt: Date }

这个方案精准且易维护,只要实体类继承基类,就能自动被识别并排除,完全符合ORM场景需求。

方案2:用品牌类型标记自定义实体

如果没法依赖统一基类,可以给实体类加一个独特的品牌属性,在类型层面区分自定义实体和其他对象:

// 定义品牌标记类型
type EntityBrand = { __isOrmEntity: true }
type EntityInstance<T> = T & EntityBrand

// 实体类实现品牌标记
class A implements EntityInstance<A> {
    __isOrmEntity = true as const
}

class B implements EntityInstance<B> {
    __isOrmEntity = true as const
}

type MyType = {
    a: A,
    b: B,
    n: number,
    str: string
}

// 排除带品牌标记的属性
type ExcludeEntityInstances<T> = {
    [K in keyof T as T[K] extends EntityBrand ? never : K]: T[K]
}

type test = ExcludeEntityInstances<MyType>
// 最终类型:{ n: number; str: string }

这个方案需要修改实体类,但能在TS的结构类型系统中精准区分自定义实体和普通对象、内置类型。

方案3:排除非原始类型(适合简单场景)

如果你的列属性只有原始类型(number/string/boolean等),可以直接排除所有非原始类型的属性:

type Primitive = string | number | boolean | null | undefined | symbol | bigint

type ExcludeNonPrimitiveProperties<T> = {
    [K in keyof T as T[K] extends Primitive ? K : never]: T[K]
}

type test = ExcludeNonPrimitiveProperties<MyType>
// 最终类型:{ n: number; str: string }

注意:这个方案会把Date、数组、普通对象等所有对象类型都排除,只适合列属性纯为原始类型的简单场景。

内容的提问来源于stack exchange,提问作者Alex K.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 23:02:07