目前(从类型脚本5.4开始),不可能以编译器可以准确地验证为类型安全的方式来实现返回conditional types的generic functions.在microsoft/TypeScript#33912有一个长期开放的特性请求,希望有更好的东西,看起来这个问题是microsoft/TypeScript#57475的TypeScrip5.5路由图的一部分,在microsoft/TypeScript#56941有一个拉请求来解决它.如果拉请求或类似的请求被合并,那么您的代码可能会突然开始工作,就像您编写的代码一样,没有错误.
除非发生这种情况,否则,如果您希望在运行时保持函数不变,您将需要使用类似type assertions的代码来防止编译器错误, Select 不使用可能的漏报进行判断,而不是使用漏报进行过于严格的判断:
async function mainFunction<T extends boolean>(param: T): Promise<ConditionalFooBar<T>> {
if (param === true) {
return {
propA: "a string",
propB: "a string"
} as ConditionalFooBar<T>
}
return {
propA: "a string",
propC: "a string"
} as ConditionalFooBar<T>
}
}
如果您现在需要编译器验证的类型安全,则需要重构,从使用条件类型转向使用indexed access types,其中您的操作是根据属性查找而不是if
/else
块编写的.例如:
interface X {
true: Foo;
false: Bar;
}
async function mainFunction<T extends boolean>(param: T) {
const x: X = {
get true() {
return {
propA: "a string",
propB: "a string"
}
},
get false() {
return {
propA: "a string",
propC: "a string"
}
}
}
return x[`${param}`]
}
在这里,您可以看到X
接口只是从string "true"
到Foo
以及从string "false"
到Bar
的映射.在函数内部,我们有一个X
类型的值x
,其中getters表示我们不必提前计算true
和false
分支.我们用template literal string来索引X
,其中true
或false
被序列化为"true"
或"false"
.
如果判断mainFunction()
的调用签名,则为:
// function mainFunction<T extends boolean>(param: T): Promise<X[`${T}`]>
其返回索引访问,其中关键字是对应于序列化的boolean
的template literal type.您可以看到它以您想要的方式工作:
const a = mainFunction(true);
// ^? Promise<Foo>
const b = mainFunction(false);
// ^? Promise<Bar>
因此,当您调用编译器时,它会自动推断您所期望的类型.但这个版本肯定比最初的实现更奇怪.仅仅为了类型安全,在作为一对getter实现的对象中查找序列化的布尔键可能不值得.
Playground link to code个