条件类型 ReturnType 函数签名重载解析

Conditional types ReturnType function signature overload resolution

我正在尝试使用 typescript 2.8 的新 conditional types,(尚未发布版本 2.8.0-dev.20180307)我不知道这是错误还是误用.我的重点是我对 MockedImplementation<F> 的声明,它可以是匹配 F 的完整函数,F 的 return 类型,如果 return 类型的 F 是一个 Promise,那么它也可能是 promise 解析的内容——所有内容都将相应地由 mockIt().

包装
type MockedImplementation<F> = 
    F | // The function signature
    ((ReturnType<F extends (...args: any[]) => any ? F : any>) extends infer T 
        ? T extends Promise<infer R> 
            ? (T | R) // Or the promise or just the type that the promise resolves to
            : T // or whatever type this is
        : never);

interface Wrapped<T> {
    result: T
}

function mockIt<F>(pretend : MockedImplementation<F>) : F {
    throw new Error('Not Implemented'); // doesn't matter
}

interface SomeOperationA {
    (parameters : { 'a': number[], 'b'?: string }) : Promise<string>;
}

mockIt<SomeOperationA>(() => Promise.resolve('hello')); //  OK
mockIt<SomeOperationA>(Promise.resolve('hello')); //  OK
mockIt<SomeOperationA>('hello'); //  OK
mockIt<SomeOperationA>(42); //  Type error.

mockIt of SomeOperationA 有效,可能直接是因为没有函数签名覆盖。但是 SomeOperationBmockIt 失败了:

interface SomeOperationB {
    (parameters : { 'a': number[], 'b'?: string }) : Promise<string>;
    (parameters : { 'a': number[], 'b'?: string }, rawResponse : true) : Promise<Wrapped<string>>;
    (parameters : { 'a': number[], 'b'?: string }, rawResponse : false) : Promise<string>;
}

mockIt<SomeOperationB>(() => Promise.resolve('hello')); // ❌ Type 'string' is not assignable to type 'Wrapped<string>'.
mockIt<SomeOperationB>(Promise.resolve('hello')); //  OK
mockIt<SomeOperationB>('hello'); //  OK
mockIt<SomeOperationB>(42); //  Type error.

似乎是交叉类型而不是联合类型?但我敢肯定它比那更细微。

我在某处看到一条关于“考虑最后一个重载,因为它可能是最普遍的重载”的注释,但我认为它在这里不相关,因为它的行为似乎并不重要。

编辑

@jcalz 说的有道理:

interface SomeOperationB {
    (wrapped : true) : Promise<Wrapped<string>>;
    (wrapped : false) : Promise<string>;
}

interface Wrapped<T> { result: T }

declare function acceptSomeOperationB(x: SomeOperationB): void;

acceptSomeOperationB(() => Promise.resolve('hello')); // ❌ Type 'string' is not assignable to type 'Wrapped<string>'.
acceptSomeOperationB(() => Promise.resolve({ result: 'hello' })); // ❌ Type '{ result: string; }' is not assignable to type 'string'.

没有条件类型的问题的较小再现:

declare function acceptSomeOperationB(x: SomeOperationB): void;
acceptSomeOperationB(() => Promise.resolve('hello')); // ❌ Type 'string' is not assignable to type 'Wrapped<string>'.

很明显,TypeScript 不认为箭头函数与 SomeOperationB 兼容,因为它无法满足其中一个重载签名。实际上,如果您将 true 作为第二个参数传递给该函数,它将 而不是 return 和 Promise<Wrapped<string>>,这是第二个签名所要求的SomeOperationB

一旦你决定了如何解决这个问题,它就应该开始工作了(或者你至少可以继续处理条件类型的问题。)

祝你好运。