我试图将一个条件类型转换成一个基于“值”(JS变量,而不是类型本身)的不同条件类型,或者可能与相关的泛型类型组合。我可能会删除其中一个类型,这样我就不需要进行转换,但是我正在寻找这种方式是否可行。
它可能归结为基于值/变量来表达Payload extends void ? { ... } : { ... };
,而不使用as
或不同的TS转义阴影。我不知道这是否可能。
参见TS Playground
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 };
// how to convert PayloadWithMeta to NewPayloadFormat?
function converter<Payload>(payloadWithMeta: PayloadWithMeta<Payload>) {
// extract config
const config = (() => {
if ("payload" in payloadWithMeta) {
const { payload, ...config } = payloadWithMeta;
return config as CustomConfig;
}
return payloadWithMeta;
})();
// how do we apply the conditional types from values?
return ("payload" in payloadWithMeta
? { payload: payloadWithMeta.payload, config }
: { config }
) /* as NewPayloadFormat<Payload> <- this is required otherwise error */ satisfies NewPayloadFormat<Payload>;
}
// usage
const result = converter({
payload: "some payload"
});
1条答案
按热度按时间yhived7q1#
TypeScript无法对依赖于generic类型参数的conditional types执行太多有用的推理,例如
converter()
主体中的PayloadWithMeta<P>
或NewPayloadFormat<P>
。编译器通常会将泛型条件类型视为 opaque,并且每当您尝试为其分配任何尚未知道的相同类型时都会抱怨。如果类型只是“等价”而不是完全相同,则赋值将失败。或者,编译器将决定通过扩展泛型类型参数到它们的约束来“过早地评估”类型,这可能会丢弃您需要的信息,也可能不会丢弃。现在,编译器无法对泛型函数中的类型参数进行足够的细化,以“智能”地处理条件类型。目前,这是TypeScript的一般限制。可能GitHub中最适用的开放问题是microsoft/TypeScript#33912,它专门讨论了一个函数能够返回一个没有类型Assert的通用条件类型。所以,现在,如果你想保持你的实现原样,你需要使用类型Assert或类似的东西(例如,单调用签名重载),并从本质上放弃编译器验证的类型安全。只要您理解验证类型安全的角色已经转移到您身上,这可能是好的。
如果您想要更多的编译器验证类型安全性,对于这个特定的示例,您可以重构为一个版本,使泛型条件部分在整个过程中完全不透明。也就是说,将
PayloadWithMeta<P>
和NewPayloadFormat<P>
都写成一个通用Payload<P>
类型的函数,您只需随意移动该类型,而无需尝试检查。可能像这样:这是因为在
converter()
中的destructuring assignment中,config
被视为类型CustomConfig
,而payload
被视为类型Payload<P>
,不管是什么。然后,我们将spreadpayloadWithMeta
返回到一个新的对象,该对象具有config
属性,表示为交集。在任何时候,编译器都不会试图理解Payload<P>
内部发生了什么。无论如何,这种重构对于其他示例可能并不总是可行的,或者可能不适合特定的用例。所以这里的一般答案是不幸的,对于泛型条件类型,你将比编译器更聪明,并且应该相应地使用类型Assert,至少从TypeScript 5.0开始。
Playground链接到代码