打字稿:启用strictNullChecks时,带有可选参数的重载不起作用

时间:2019-06-24 16:16:40

标签: typescript typescript-typings

我正在使用泛型和重载的组合来键入类,但是启用strictNullChecks时,重载中的可选参数有问题。如何启用strictNullChecks才能使用它?

一个简单的示例测试用例失败,并显示“无法将类型'T | undefined'的参数分配给类型'undefined'的参数。”仅在启用strictNullChecks时:

class BaseClass { }
class BaseClass2 { }

class Test<T extends BaseClass, R extends BaseClass2> {
    test(a: T): R;
    test(a?: undefined): undefined;
    test(a?: any) {
        return a;
    }

    test2(b?: T) {
        // Errors only when strictNullChecks is enabled (can be switched on in the options tab above)
        return this.test(b) 
    }
}

const testClass = new Test<BaseClass, BaseClass2>()

// desired ouput
const output1: undefined = testClass.test2()
const output2: BaseClass2 = testClass.test2(new BaseClass())

或在打字稿游乐场:https://www.typescriptlang.org/play/index.html#src=class%20BaseClass%20%7B%7D%0D%0A%0D%0Aclass%20BaseClass2%20%7B%20%7D%0D%0A%0D%0Aclass%20Test%3CT%20extends%20BaseClass%2C%20R%20extends%20BaseClass2%3E%20%7B%0D%0A%20%20%20%20testM(a%3A%20T%20%7C%20undefined%2C%20b%3A%20R)%20%7B%0D%0A%20%20%20%20%20%20%20%20if%20(a)%20%7B%0D%0A%20%20%20%20%20%20%20%20%20%20%20%20return%3B%0D%0A%20%20%20%20%20%20%20%20%7D%0D%0A%20%20%20%20%20%20%20%20return%20b%3B%0D%0A%20%20%20%20%7D%0D%0A%0D%0A%20%20%20%20testM2(d%3A%20R%2C%20c%3F%3A%20T)%20%7B%0D%0A%20%20%20%20%20%20%20%20return%20this.testM(c%2C%20d)%20%0D%0A%20%20%20%20%7D%0D%0A%7D%0D%0A%0D%0Aconst%20test%20%3D%20new%20Test%3CBaseClass%2C%20BaseClass2%3E()%0D%0A%0D%0Aconst%20output1%3A%20undefined%20%3D%20test.testM2(new%20BaseClass2()%2C%20undefined)%0D%0Aconst%20output2%3A%20BaseClass2%20%3D%20test.testM2(new%20BaseClass2()%2C%20new%20BaseClass())

编辑(2019年6月25日14:20):更新代码段以反映所需的类型输出

1 个答案:

答案 0 :(得分:0)

这是启用--strictNullChecks的正确且定义明确的结果。

TypeScript的设计师Anders Hejlsberg在this talk中对其进行了精彩的解释。这是第三名,但是绝对值得一看。

简而言之,--strictNullChecks。更改类型之间的关系,以使值nullundefined分别是两个新的单例类型nullundefined的成员,并且不再是任何其他类型的组成部分。 / p>

因此,给定T | undefined,第一个签名

test(a: T)

不再是候选人。而且,至关重要的是,第二个签名

test(a: undefined)

不再是候选者,因为它的参数可能是T,而T不是undefined

幸运的是,该解决方案实际上是更少的代码并且更易于理解

test(a: T | undefined) {
    return something;
}

联合类型表示a是属于所有类型T和类型undefined的联合的所有可能值的成员。

采用所有可能参数类型的并集类型的单个签名与一组不同的重载签名之间的关键区别是参数和返回类型的相关性。

具体地说,

declare function f(x: A): B;
declare function f(x: T): U;

不等于

declare function f(x: A | T): B | U;

在接受和拒绝方面有所不同。请以此playground为例。

但是,没有--strictNullChecks时,重载仅在使用已知完全为undefined的值时有意义,因为每个T都隐含T | undefined

您可以返回使用重载,从而重新获得对参数类型的控制,以返回类型相关性,但只能通过在调用站点上引入其他信息来实现,例如

test2(b?: T) {
    if (b) { 
      return this.test(b);
    } else {
      return this.test(b);
    }
}