我有两个非常相似的函数,但打字稿只能推断其中一个的返回值:
interface URItoKind<A> {}
type URIS = keyof URItoKind<any>;
type Kind<URI extends URIS, A> = URItoKind<A>[URI];
const URI = "Option";
export type URI = typeof URI;
interface URItoKind<A> {
readonly [URI]: Option<A>;
}
class Option<A> {
static of: <A>(a: A) => Option<A>;
}
function test1<F extends URIS, A, B>(f: (a: A) => Kind<F, B>): Option<B>;
function test2<F extends URIS>(): <A, B>(f: (a: A) => Kind<F, B>) => Option<B>;
const func = (x: number) => Option.of(x.toString()); // (x: number) => Option<string>
const r1 = test1(func); // Option<unknown> - Doesn't work
const r2 = test2()(func); // Option<string> - Works Fine
这是由于 TypeScript 有时为了性能而“懒惰”的方式造成的。
function test1<F extends URIS, A, B>(f: (a: A) => Kind<F, B>): Option<B>;
一旦您调用
test1(func)
,TypeScript 就会尝试推断类型。问题是 TS 采用 F extends URIS
并用它来推断 Kind<F, B>
。 TypeScript 在某些情况下会针对性能进行优化,在这种情况下,TypeScript 会变得“懒惰”,以便更快地推断类型。
function test2<F extends URIS>(): <A, B>(f: (a: A) => Kind<F, B>) => Option<B>;
在这种情况下,你必须打电话。第一次调用会触发 TypeScript 尝试推断 F,但没有足够的信息,因此 TypeScript 会等到第二次调用。在第二次调用中,没有像
F extends URIS
这样的“提示”,因此 TypeScript 推断出 F
,但这一次它不会以懒惰的方式执行此操作。它会遍历所有通用类型,直到找到真正的答案。
您可以消除通用约束
F extends URIS
以防止打字稿“懒惰”,而不提示意味着TS需要一直向下推断类型。
declare function test1<A, B>(f: (a: A) => Kind<URIS, B>): Option<B>;
declare function test2(): <A, B>(f: (a: A) => Kind<URIS, B>) => Option<B>;
现在两者都按预期工作:
const func = (x: number) => Option.of(x.toString()); // (x: number) => Option<string>
const r1 = test1(func); // Option<string> - Works Fine
const r2 = test2()(func); // Option<string> - Works Fine