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

如何避免TypeScript条件映射类型结果中混入{}类型?

TypeScript 类型工具优化问题

原始定义与问题

我们定义了如下接口:

interface A {
    a: string
    b: string
    c?: number
    d?: number
}

同时定义了类型B<T>,目的是将T中类型为string的键设为可选,类型为number的键设为必填:

type B<T> = {                                      
    [K in keyof T as T[K] extends (number|undefined) ? K : never]-?: T[K]                      
} & {
    [K in keyof T as T[K] extends (string|undefined) ? K : never]+?: T[K] 
}

/* 正常生成的类型:
type B<A> = {
    c: number;
    d: number;
} & {
    a?: string | undefined;
    b?: string | undefined;
}
*/

但当接口仅包含其中一种类型时(比如仅含string类型键的A1):

interface A1 {
    a: string
    b: string
}

/* 生成的类型变为:
type B<A1> = {} & {
    a?: string | undefined;
    b?: string | undefined;
}
*/

这会导致非预期类型可以被赋值给B<A1>,例如:

const b: B<A1> = "We don't want this to happen." // 此处本该报错,但现在不会

解决方案

修改B<T>类型,通过条件类型判断是否存在某一类键,避免生成包含{}的交叉类型:

// 提取T中属于number/undefined类型的键
type RequiredNumberKeys<T> = { 
  [K in keyof T]: T[K] extends (number | undefined) ? K : never 
}[keyof T]

// 提取T中属于string/undefined类型的键
type OptionalStringKeys<T> = { 
  [K in keyof T]: T[K] extends (string | undefined) ? K : never 
}[keyof T]

type B<T> = 
  // 若没有number类型的键,直接返回可选string键的类型
  RequiredNumberKeys<T> extends never
    ? { [K in OptionalStringKeys<T>]?: T[K] }
    // 若没有string类型的键,直接返回必填number键的类型
    : OptionalStringKeys<T> extends never
      ? { [K in RequiredNumberKeys<T>]-?: T[K] }
      // 两种键都存在时,返回交叉类型
      : { [K in RequiredNumberKeys<T>]-?: T[K] } & { [K in OptionalStringKeys<T>]?: T[K] }

修改后,B<A1>会直接生成为:

{
    a?: string | undefined;
    b?: string | undefined;
}

此时再执行const b: B<A1> = "We don't want this to happen."就会触发TypeScript的类型错误,符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:21:36