首页 > 解决方案 > 在这种情况下,TypeScript 类型系统是否过于宽松?

问题描述

我想我遇到了一个似乎应该导致 TS 编译器出错的场景(但事实并非如此),我希望有人能解释原因。

在下面的代码中,我将一个接口传递给它接受Foo的函数。frobnicator然后,在 的正文中frobnicator,我“删除”了该字段bar.y。终止后frobnicator,类型系统允许我打印bar.y(没有错误),尽管y不再存在。

类型系统不应该禁止我传递foo到,frobnicator因为现在foo不再实现Foo接口并且 TS 认为它实现了吗?

interface Foo {
    bar: { x: number, y: number };
}

function frobnicator(thing: { bar: { x: number } }) {
    thing.bar = { x: 1 }; // "remove" bar.y
}

const foo: Foo = { bar: { x: 1, y: 2 } };
frobnicator(foo); // The implementation "removes" bar.y
console.log(foo.bar.y); // TypeScript does not error despite bar.y missing

标签: typescripttype-systemsstructural-typing

解决方案


您的界面与函数内的代码无关,因为即使您的 const 的类型是 Foo,该函数接受的不是 foo,而是未命名的类型{ bar: { x: number } }。由于 Typescript 在编译时而不是在运行时工作,它不可能知道您的函数已被删除y,因为函数内部的类型没有y,而它外部Foo的类型是函数参数类型的超类型,因此它的实例适合参数的类型。所以,一方面你的函数从对象中删除了一个字段,但你没有告诉编译器它是被禁止的,因为函数内部的类型与你传入的类型不同,所以外部它仍然认为你有y. 在像 Java 这样的语言之后,这似乎违反直觉,但事实就是如此。为避免此类问题,您应该使用正确的类型,例如,在函数中使用您的接口类型。


推荐阅读