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

TypeScript如何改写TypeOfShape使形状推导的可选必填属性合并而非交叉

解决方案

你可以通过一个类型扁平化工具泛型包裹原有计算结果,或者直接重构TypeOfShape的映射逻辑,实现单个对象类型的展示效果:

最简修改方案

仅需新增一个Simplify工具泛型,外层包裹原有逻辑即可,不会修改原有功能的兼容性:

// 新增类型扁平化工具,作用是合并交叉类型为单个对象
type Simplify<T> = { [K in keyof T]: T[K] } & {}

class Type<T> {
  // just a marker
  declare readonly _type: T;
}

type AnyType = Type<any>;

class Optional<T extends AnyType> {
  constructor(readonly inner: T) {}
}

type Shape = { [k: string]: AnyType | Optional<AnyType> };

// 原有逻辑不变,外层套Simplify即可
type TypeOfShape<T extends Shape> = Simplify<
  & { [K in keyof T as T[K] extends AnyType ? K : never]: T[K] extends AnyType ? T[K]["_type"] : never }
  & { [K in keyof T as T[K] extends Optional<AnyType> ? K : never]?: T[K] extends Optional<AnyType> ? T[K]["inner"]["_type"] : never }
>;

const foo = {bar: new Type<number>(), baz: new Optional(new Type<string>())} as const;
// 推导结果为 { readonly bar: number; readonly baz?: string }
type Foo = TypeOfShape<typeof foo>;

const foo2 = {bar: new Type<number>()} as const;
// 推导结果为 { readonly bar: number }
type Foo2 = TypeOfShape<typeof foo2>

原理说明

Simplify泛型的作用是触发TS的类型即时计算,将分散的交叉类型片段合并为单一对象结构展示,同时完全保留原有类型的属性规则和兼容逻辑,和原有实现的功能完全一致。

如果不想额外引入独立的工具泛型,也可以直接将合并逻辑内嵌到TypeOfShape中,效果完全相同:

type TypeOfShape<T extends Shape> = ({
  [K in keyof T as T[K] extends AnyType ? K : never]: T[K]['_type']
} & {
  [K in keyof T as T[K] extends Optional<AnyType> ? K : never]?: T[K] extends Optional<AnyType> ? T[K]['inner']['_type'] : never
}) extends infer R ? { [K in keyof R]: R[K] } : never

内容的提问来源于stack exchange,提问作者user3612643

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 15:06:05