这里最简单的方法可能是将strBuilder()
的返回类型表示为overloaded function类型:
interface StringBuilder {
(str: string): StringBuilder;
(): string;
}
function strBuilder(str: string): StringBuilder {
return function next(str2?: string) {
if (typeof str2 === "string") {
return strBuilder(str + str2)
} else {
return str
}
} as StringBuilder // <-- assert
}
我们需要assertion,因为编译器不能准确地验证函数实现是否符合重载的调用签名(参见How to correctly overload functions in TypeScript?).
无论如何,上述代码都会编译并产生所需的调用行为:
const str = strBuilder("Hello, ")("World")();
console.log(str.toUpperCase())
您could将其编写为generic conditional type,但这更复杂,也不一定更可取(这真的取决于用例):
interface StringBuilder {
<S extends string | undefined = undefined>(str?: S):
S extends string ? StringBuilder : string;
}
这与您的版本类似,只是我只是直接表示返回类型(这样就不会使用ReturnType
,这样就不必担心在原始函数和返回函数内使用相同的S
,这不是您想要的),如果有人在没有参数的情况下调用函数,我给出的值是default到S
(否则它将回退到string | undefined
,这是没有用的).
strBuilder()
的主体中仍然需要类型断言,因为编译器也无法准确验证函数实现是否符合泛型条件返回类型;请参见microsoft/TypeScript#33912.因此,在类型安全方面,您不会得到或失go 任何东西.
无论如何,对于上面的示例,它的行为是相同的:
const str = strBuilder("Hello, ")("World")();
console.log(str.toUpperCase())
我可能会坚持使用重载,除非您遇到一些具体的麻烦.
Playground link to code个