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

TypeScript如何将Option类型属性转为可选属性?

如何修改UnwrappedOptionsType工具类型,将Option类型的属性转为可选?

我正在实现TypeScript工具类型UnwrappedOptionsType,用于解包sniptt的Option monad。当前代码能正确推断Option内部的类型,但无法将对应属性转为可选状态。

举个实际场景:

// 假设Option的基础定义
type Option<T> = T | null | undefined;

interface SignUpRequest {
  email: Option<string>;
  password: string;
}

// 现有工具类型实现
type UnwrappedOptionsType<T> = {
  [K in keyof T]: T[K] extends Option<infer U> ? U : T[K];
};

// 实际得到的类型:{ email: string; password: string }
// 期望得到的类型:{ email?: string | undefined; password: string }

目前的问题是,email属性在解包后仍为必填的string,但需要它变成可选的string | undefined,同时保留非Option类型属性的必填性。


解决方案

要实现需求,需要在映射类型中区分Option与非Option属性,对前者标记可选性并解包内部类型,后者保持原有状态。可以通过条件映射+交叉类型实现:

type Option<T> = T | null | undefined;

type UnwrappedOptionsType<T> = {
  // 保留非Option类型的属性,维持原有可选性与类型
  [K in keyof T as T[K] extends Option<any> ? never : K]: T[K];
} & {
  // 处理Option类型的属性:标记为可选,并解包内部类型
  [K in keyof T as T[K] extends Option<any> ? K : never]?: T[K] extends Option<infer U> ? U : never;
};

// 测试验证
interface SignUpRequest {
  email: Option<string>;
  password: string;
  nickname?: Option<string>; // 原本就可选的Option属性
}

// 最终推断结果(合并后):
// {
//   password: string;
//   email?: string | undefined;
//   nickname?: string | undefined;
// }

代码说明

  1. 第一个映射类型:通过as从句过滤掉Option类型的键,保留非Option属性的原有特性(必填/可选、类型)。
  2. 第二个映射类型:过滤出Option类型的键,用?标记为可选,同时通过infer U解包Option内部的泛型类型。
  3. 交叉类型&将两部分结果合并,得到符合预期的最终类型。

如果追求更清晰的逻辑,可以先拆分不同类型的键再处理:

// 辅助工具:提取T中属于Option类型的键
type OptionKeys<T> = {
  [K in keyof T]: T[K] extends Option<any> ? K : never;
}[keyof T];

// 辅助工具:提取T中不属于Option类型的键
type NonOptionKeys<T> = Exclude<keyof T, OptionKeys<T>>;

type UnwrappedOptionsType<T> = {
  [K in NonOptionKeys<T>]: T[K];
} & {
  [K in OptionKeys<T>]?: T[K] extends Option<infer U> ? U : never;
};

这种写法通过拆分键的类型,让逻辑更直观,维护性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 06:21:28