Typescript 允许为未定义的参数传递非未定义的值
我在 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
答案#1
来自TypeScript规范
未定义类型是所有类型的子类型。这意味着未定义被认为是所有原始类型、对象类型、联合类型、交叉类型和类型参数的有效值。
让我们考虑 TypeScript 将如何解决以下情景:
class Base{ b: number}
class Derived extends Base{ c: number}
let GetDerived: IEndpoint<Derived>;
call(GetConsumer, new Base());
在 call 中,泛型参数 RequestType 有两种可能的类型:Derived(基于第一个参数)和 Base(基于第二个参数)。TypeScript 会为两者选择共同的基类型,因此 RequestType 将是 Base。
现在让我们考虑你的一个例子:
call(GetConsumer, 1);
与第一个示例类似,call 中的 RequestType 可以是:undefined(基于第一个参数)和 number(例如基于第二个参数)。由于 undefined 是所有类型的子类型,它也是 number 的子类型,因此最合适的公共基类型是 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
