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()
            ]);
    }
}

constfoobarbaz表明条件类型工作正常。 函数调用bundle({ type: 'foo' })也会正确提供类型 Promise<void[]> 如果您使用 TS 操场并将鼠标悬停在其上。为什么它不适用于返回值?我还尝试了这是否是由于 TypeScript 无法通过向函数添加 kind: T 参数来推断T引起的,但没有更改。断言Promise.resolve() PromiseResult<T>工作正常。

Typescript 通常不会让你对条件类型做太多事情,只要它们仍然有未解析的条件类型(就像PromiseResult<T>中的情况T(

此外,在这种情况下,您假设缩小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()
            ]);
    }
}

最新更新