我想我遇到了一种情况,它似乎会导致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发布于 2021-09-18 22:10:03
答案是肯定的,您的代码显示了类型记录类型系统的不健全行为,这正是您在问题中描述的原因。
答案也是否定的,您没有“破坏”类型记录的类型系统,因为这种不健全的行为是预期的行为。打字本不是完全正确的,也不是完全正确的;引用医生们的话
类型记录的类型系统允许在编译时无法知道的某些操作是安全的.当一个类型的系统具有这个属性时,就会说它不是“声音”。我们仔细考虑了TypeScript允许不健全行为的地方,在整个文档中,我们将解释这些行为发生的地点以及它们背后的激励场景。
类型抄本的设计者明确选择了在可靠性和有用性之间取得平衡;一个完全健全的类型系统不会将{bar: {x: number, y: number}}视为{bar: {x: number}}的一个子类型,但是真正的Javascript代码通常会传递具有比函数实际访问的更多属性的对象,因此严格合理的行为对于编写真实Javascript代码的开发人员不会有多大用处。
发布于 2021-09-18 21:42:46
您的接口与函数中的代码无关,因为即使您的const的类型是Foo,函数接受的不是foo,而是未命名的类型{ bar: { x: number } }。由于类型记录在编译时工作,而不是在运行时,它不可能知道删除的函数y中的类型没有y,而外部的类型是Foo,它是函数参数类型的超级类型,因此它的实例适合于参数的类型。因此,一方面,函数从对象中删除一个字段,但是您没有告诉编译器它是被禁止的,因为函数中的类型与传入的类型不同,所以在外部,它仍然认为您有y。在Java这样的语言之后,这似乎有悖常理,但事实就是如此。为了避免这些问题,您应该使用适当的类型,例如,在函数中使用您的接口类型。
发布于 2021-09-18 21:38:28
该函数只需要一个嵌套字典。不是钥匙的确切数量。
https://stackoverflow.com/questions/69238613
复制相似问题