基于类型保护推断函数的返回类型

3

我有以下定义:

function foo(arg: number[] | number) {
    if (Array.isArray(arg)) {
        return [1]
    }

    return 0
}

我希望TypeScript能够自动推断出返回类型。由于类型保护isArray()可以知道arg是否为数组,并将返回类型显示为number[]。然而,使用foo(...)即使传递一个数组,其返回值仍显示为number[] | 0
  foo([]).push() // error because push doesnt exist on type 0

这是设计限制、BUG、尚未实现还是其他问题?

3个回答

5
我无法确定这是设计限制,但我看到像jcalzTitian Cernicova-Dragomir这样的专家引用了各种地方,有时类型推断受到限制并不是因为它不能做我们想要的事情,而是因为做这件事会太昂贵(在运行时成本或编译器中的代码复杂度方面)。我认为这适合于该类别。
您可能已经知道,但对于您的特定示例,您可以使用重载来获得所需结果:
function foo(arg: number[]): number[];
function foo(arg: number): number;
function foo(arg: number[] | number) {
    if (Array.isArray(arg)) {
        return arg.map(v => v * 2);
    }
    
    return arg * 2;
}
foo([]).push(10);

操场链接


2
是的,我知道,但如果您不需要定义所有这些重载,那将是多么酷啊... - Fuzzyma
1
GitHub问题:https://github.com/microsoft/TypeScript/issues/40802 - Fuzzyma

0

我认为你要找的是重载

按照以下方式使用重载将解决你的问题:


function foo(arg1: number[]): number[];
function foo(arg1: number): 0;
function foo(arg1: any){
    if(Array.isArray(arg1)){
        return [0]
    }

    return 0
}

0

另一种选择是泛型

function foo<T extends number[] | number>(arg:T):T {
    if (Array.isArray(arg)) { return [1] as T }
    return 0 as T
}

foo([] as number[]).push(10) // works
foo(42).push(10) // error as expected

foo的使用者获得完美的类型。在内部,我们使用类型断言,因为返回的值不是sound。调用者指定了T的形状,所以我们无法知道它到底是什么。

想象一下,数字42迷的俱乐部拥有foo - 所有其他数字都是mehh... :)

function foo<T extends number[] | number>(arg:T):T {
    if (Array.isArray(arg)) { return [1]}
    return 1
}
foo(42); // T instantiated as 42

显然这是不能容忍的。

虽然这个例子是人为制造的 - 但俱乐部听起来很不错 - 你可能明白了,为什么第一个例子需要type asserted

更现实、更合理的示例:
function ensureDefined<T extends number[] | number>(initializer: () => T, arg?: T): T {
    if (arg === undefined || Array.isArray(arg) && arg.length === 0) {
        return initializer()
    } else return arg
}

ensureDefined(() => [42], [] as number[]).push(10) // works
ensureDefined(()=> 42, undefined) // error as expected

这里是一个游乐场


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接