我在typescript playground中尝试了以下代码,并打开了所有选项。
我希望TS编译器只允许第一个call()
有效。但是这四个都是。
通过将鼠标悬停在通话上,我看到它们输入为call<"String"|undefined>
。
这里发生了什么?有没有办法强行检查?
interface IEndpoint<RequestType> { }
export const GetConsumer: IEndpoint<undefined> = {};
function call<RequestType>(rpc: IEndpoint<RequestType>, request: RequestType) {}
call(GetConsumer, undefined);
call(GetConsumer, null); // should not be allowed
call(GetConsumer, 1); // should not be allowed
call(GetConsumer, "String"); // should not be allowed
答案 0 :(得分:4)
来自打字稿specification
未定义类型是所有类型的子类型。这意味着undefined被认为是所有基本类型,对象类型,联合类型,交集类型和类型参数的有效值。
让我们考虑打字稿如何解决以下情况:
class Base{ b: number}
class Derived extends Base{ c: number}
let GetDerived: IEndpoint<Derived>;
call(GetConsumer, new Base());
RequestType
中的通用参数call
有两种可能的类型:Derived
(基于第一个参数)和Base
(基于第二个参数)。 Typescript将为两者选择公共基本类型,因此RequestType
将为Base
。
现在让我们考虑一下你的例子:
call(GetConsumer, 1);
与第一个示例类似,RequestType
中的call
可以是:undefined
(基于第一个参数)和number
(例如,基于第二个参数) 。由于undefined
属于所有类型的子类型,因此它也是数字的子类型,因此最佳公共基类型为number
。
如果您正在寻找一种不允许最后两次通话的类型,void
可以这样做,因为:
Void类型的唯一可能值是null和undefined。 Void类型是Any类型的子类型和Null和Undefined类型的超类型,但是Void与所有其他类型无关。
export const GetConsumer: IEndpoint<void> = {
};
call(GetConsumer, undefined);//still ok
call(GetConsumer, null); // still ok
call(GetConsumer, 1); // error
call(GetConsumer, "String"); // error