如何在TypeScript中基于值将条件类型转换为另一种条件类型?
基于值转换TypeScript条件类型(无需类型断言)
你想要实现的是:在不使用as等TypeScript类型断言的前提下,通过运行时的值判断,把PayloadWithMeta<Payload>类型转换成NewPayloadFormat<Payload>类型。核心难点在于TypeScript无法自动将泛型条件类型和运行时的属性检查直接关联起来,下面提供两种可行的解决方案:
方案一:使用函数重载明确类型映射
函数重载可以直接定义不同输入类型对应的输出类型,让TypeScript自动匹配并推断结果,完全不需要断言:
interface CustomConfig {} export type PayloadWithMeta<Payload> = Payload extends void ? CustomConfig : { payload: Payload } & CustomConfig; type NewPayloadFormat<Payload> = Payload extends void ? { config: CustomConfig } : { payload: Payload; config: CustomConfig }; // 重载签名:分别定义两种分支的输入输出 function converter(payloadWithMeta: CustomConfig): { config: CustomConfig }; function converter<Payload>(payloadWithMeta: { payload: Payload } & CustomConfig): { payload: Payload; config: CustomConfig }; // 函数实现 function converter(payloadWithMeta: CustomConfig | ({ payload: unknown } & CustomConfig)) { if ("payload" in payloadWithMeta) { const { payload, ...config } = payloadWithMeta; return { payload, config }; } return { config: payloadWithMeta }; } // 使用示例 const resultWithPayload = converter({ payload: "some payload" }); // 自动推断类型:{ payload: string; config: CustomConfig } const resultWithoutPayload = converter({} as CustomConfig); // 自动推断类型:{ config: CustomConfig }
方案二:自定义类型守卫配合泛型函数
如果想保留泛型函数的写法,可以通过自定义类型守卫帮TypeScript在控制流中缩小类型范围,再用satisfies验证返回值符合目标类型:
interface CustomConfig {} export type PayloadWithMeta<Payload> = Payload extends void ? CustomConfig : { payload: Payload } & CustomConfig; type NewPayloadFormat<Payload> = Payload extends void ? { config: CustomConfig } : { payload: Payload; config: CustomConfig }; // 自定义类型守卫:判断输入是否属于带payload的分支 function hasPayload<T>(value: PayloadWithMeta<T>): value is Exclude<PayloadWithMeta<T>, CustomConfig> { return "payload" in value; } function converter<Payload>(payloadWithMeta: PayloadWithMeta<Payload>) { if (hasPayload(payloadWithMeta)) { const { payload, ...config } = payloadWithMeta; return { payload, config } satisfies NewPayloadFormat<Payload>; } else { return { config: payloadWithMeta } satisfies NewPayloadFormat<Payload>; } } // 使用示例 const result = converter({ payload: "some payload" }); // 自动推断类型:NewPayloadFormat<string>
核心原理说明
TypeScript的泛型条件类型(比如Payload extends void ? ... : ...)在函数内部是延迟解析的,单纯的"payload" in运行时检查无法让TypeScript关联到泛型的分支判断。通过重载签名直接定义类型映射,或者用类型守卫明确缩小类型范围,就能让TypeScript正确推断输出类型,从而避免使用as这类"逃逸舱"。
内容的提问来源于stack exchange,提问作者kjetilh
相关产品推荐
相关产品推荐

