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

TypeScript中类型字面量与类型参数表现差异原因及适配方案

问题解析与解决方案

为什么两种写法表现不同?

核心原因是TypeScript条件类型的「分配性」行为:

  • 当条件类型中extends的左侧是裸类型参数(比如你定义的V)时,TypeScript会对联合类型进行「分配式判断」——把联合拆成单个成员分别执行条件判断,再将结果合并成新的联合。
  • 而直接写null | undefined extends O[K]时,左侧是具体的联合字面量,不是裸类型参数,不会触发分配性,只会整体判断null | undefined这个联合是否能赋值给O[K]。

具体到你的例子:

  1. 第一种写法(直接用null | undefined):
    对TestRange的undefined属性,判断null | undefined extends undefined?显然不成立(null无法赋值给undefined),所以这个键被排除,最终结果只有"nullish"。
  2. 第二种写法(用类型参数V):
    V是null | undefined这个裸联合参数,条件V extends O[K]会被拆成两个独立判断:null extends undefined ? K : never 和 undefined extends undefined ? K : never。后者判断成立,所以"undefined"被保留,最终结果是"undefined" | "nullish"。

如何让NullishKeysIn适配联合类型参数?

要避免分配性判断,只需把裸类型参数V包裹起来(比如用元组),让extends左侧不再是裸参数,就能整体判断联合类型是否能赋值给O[K]。修改后的类型如下:

type NullishKeysIn<O, V = null | undefined> = {
  [K in keyof O]: [V] extends [O[K]]
    ? K
    : never
}[keyof O]

测试验证:

interface TestRange {
  undefined: undefined,
  nullish: null | undefined,
}

// 正确触发错误:'"undefined"' 不能赋值给 '"nullish"'
let prop1: NullishKeysIn<TestRange> = 'undefined' as const
// 正常通过
let prop2: NullishKeysIn<TestRange> = 'nullish' as const

// 自定义联合类型参数的情况也能正常工作
type CustomNullish = null | undefined | void
type CustomKeys = NullishKeysIn<{ a: void, b: null | void, c: string }, CustomNullish>
// CustomKeys 的类型是 "a" | "b"

这样修改后,无论是使用默认的null | undefined,还是传入自定义的联合类型参数,NullishKeysIn都会按照「整体判断联合是否能赋值给属性类型」的逻辑工作,符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 13:00:34