如何实现排除对象类型中类实例属性的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.
相关产品推荐
相关产品推荐

