TypeScript:条件通用类型作为函数返回值不可分配

时间:2019-05-28 10:20:09

标签: typescript

我的一个通用函数中的可分配值有问题:

interface BuildArguments<T extends string> {
    type: T;
}

type PromiseResult<T> =
    T extends 'standalone' ? Promise<void> :
    T extends 'all' ? Promise<void> :
    Promise<void[]>;    

const foo: PromiseResult<'standalone'> = Promise.resolve();
const bar: PromiseResult<'all'> = Promise.resolve();
const baz: PromiseResult<'foo'> = Promise.resolve([]);

bundle({ type: 'foo' });

function bundle<T extends string>(buildArguments: BuildArguments<T>): PromiseResult<T> {
    switch (buildArguments.type) {
        case 'standalone':
            return Promise.resolve(); // error here, not assignable to PromiseResult<T>
        case 'all':
            return Promise.resolve(); // error here, not assignable to PromiseResult<T>
        default:
            return Promise.all([ // error here, not assignable to PromiseResult<T>
                Promise.resolve(),
                Promise.resolve()
            ]);
    }
}

constfoobar表明条件类型可以正常工作。如果您使用ts游乐场并将鼠标悬停在该游乐场上,则函数调用baz也会正确提供类型bundle({ type: 'foo' })。为什么对返回值不起作用? 我还尝试了这是否是由于TypeScript无法通过向函数添加Promise<void[]>参数来推断T而导致的,但是没有任何变化。将kind: T设为Promise.resolve()很好。

1 个答案:

答案 0 :(得分:3)

只要条件脚本仍具有无法解析的条件类型(对于TPromiseResult<T>而言),Typescript通常不会让您对条件类型做太多事情

此外,在这种情况下,您假设缩小buildArguments.type会缩小T。它不是。缩小会缩小值,而不是整个类型参数。这是别无选择的方式,请考虑以下示例:

function foo<T extends string | number>(a: T, b: T) {
    if(typeof a === "string") {
        // should b be string? No
    }
}

foo<string | number>(1, "");

仅仅因为我们缩小了一个T类型的值,对其他此类变量没有影响。

最简单的解决方案是使用一个更宽松的单独实现签名,同时将公共签名保留为对调用者更有利的条件类型:

function bundle<T extends string>(buildArguments: BuildArguments<T>): PromiseResult<T>
function bundle(buildArguments: BuildArguments<string>): PromiseResult<'standalone' | 'all'> | PromiseResult<string> {
    switch (buildArguments.type) {
        case 'standalone':
            return Promise.resolve(); 
        case 'all':
            return Promise.resolve(); 
        default:
            return Promise.all([ 
                Promise.resolve(),
                Promise.resolve()
            ]);
    }
}