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

如何定义TypeScript类型:仅允许单个属性为undefined

实现恰好一个属性为undefined的类型:OneUndefined<T>

要实现这个需求,咱们可以用TypeScript的映射类型和联合特性,遍历目标类型的每个属性,为每个属性生成该属性可选为undefined,其余属性保留原类型的子类型,最后把所有子类型联合起来:

type OneUndefined<T> = {
  [K in keyof T]: Omit<T, K> & { [P in K]?: undefined }
}[keyof T];

验证示例

拿你给出的MyType测试:

type MyType = {
  a: number;
  b: number;
  c: number;
};

OneUndefined<MyType>会被TypeScript解析为:

| { a?: undefined; b: number; c: number }
| { a: number; b?: undefined; c: number }
| { a: number; b: number; c?: undefined }

完全匹配你的预期。

逆类型:恰好一个属性已定义的OneDefined<T>

和OneUndefined<T>逻辑反过来,咱们要生成恰好一个属性保留原类型,其余属性可选为undefined的联合类型:

type OneDefined<T> = {
  [K in keyof T]: Pick<T, K> & { [P in Exclude<keyof T, K>]?: undefined }
}[keyof T];

验证示例

OneDefined<MyType>的结果是:

| { a: number; b?: undefined; c?: undefined }
| { a?: undefined; b: number; c?: undefined }
| { a?: undefined; b?: undefined; c: number }
通用类型:恰好n个属性已定义的NDefined<T, n>

要支持任意数量n的通用类型,核心是先生成目标类型所有长度为n的属性键组合,再为每个组合生成对应子类型并联合。

第一步:实现生成指定长度的属性键组合

先写几个辅助工具类型,用来生成所有长度为n的属性键元组:

// 把联合类型转成元组
type UnionToTuple<T> = (
  (T extends any ? (t: T) => T : never) extends infer U
    ? (U extends any ? (u: U) => any : never) extends (v: infer V) => any
      ? [...UnionToTuple<Exclude<T, V>>, V]
      : []
    : []
);

// 从元组里生成所有长度为N的组合
type Combinations<T extends any[], N extends number, Result extends any[] = []> =
  N extends 0
    ? [Result]
    : T extends [infer First, ...infer Rest]
      ? [...Combinations<Rest, N, [...Result, First]>, ...Combinations<Rest, N, Result>]
      : [];

// 获取目标类型所有长度为N的属性键元组
type PickN<T, N extends number> = Combinations<UnionToTuple<keyof T>, N>[number];

第二步:实现NDefined<T, n>

基于上面的属性键组合,生成每个组合对应的选中n个属性保留原类型,其余可选为undefined的子类型,最后联合所有子类型:

type NDefined<T, N extends number> = {
  [K in PickN<T, N>]: Pick<T, K[number]> & { [P in Exclude<keyof T, K[number]>]?: undefined }
}[PickN<T, N>];

验证示例

NDefined<MyType, 2>的结果是:

| { a: number; b: number; c?: undefined }
| { a: number; c: number; b?: undefined }
| { b: number; c: number; a?: undefined }

对应的NUndefined<T, n>(恰好n个属性为undefined)

如果需要恰好n个属性为undefined,只需调整逻辑为选中n个属性设为可选undefined,其余保留原类型:

type NUndefined<T, N extends number> = {
  [K in PickN<T, N>]: Omit<T, K[number]> & { [P in K[number]]?: undefined }
}[PickN<T, N>];

比如NUndefined<MyType, 1>就完全等价于OneUndefined<MyType>。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:55:56