为什么TypeScript编译器不会抱怨形状不匹配的对象?

时间:2019-07-12 23:58:53

标签: typescript

对于在TypeScript中使用接口时遇到的情况,我有些困惑。我有一个接受特定形状物体的函数。

const add = (val: {n1: number, n2: number}): number => {
    return val.n1 + val.n2;
};

如果我通过将其存储在变量中来发送与形状不完全匹配的对象(例如,附加键),则编译器不会抱怨并且可以成功编译。

编译器接受以下内容:

let params = {n1: 100, n2: 200, foo: "x"};
add(params);

但是不接受这一点:

let params = {n1: 100, n2: 200, foo: "x"};
add({n1: 100, n2: 200, foo: "x"}); 
//Object literal may only specify known properties.

如TypeScript文档中所述,唯一重要的是形状而不是附加键。我想知道的是为什么第一个被接受?

2 个答案:

答案 0 :(得分:3)

Typescript没有确切类型的概念。

打字稿中的对象类型并不意味着该类型的所有实例都只有这些属性。基本继承使我们可以创建具有给定类型的子类型,并且该子类型具有更多属性。考虑:

interface I {n1: number, n2: number}
interface D extends I { foo: string }

面向对象程序设计的原则还要求我们应该能够在需要基本类型的任何地方分配一个子类型。

考虑到这些规则,这样做就不足为奇了

let params = {n1: 100, n2: 200, foo: "x"};
add(params);

由于打字稿类型系统是结构性的,因此它较少依赖名义继承,而更多地依赖类型的形状。 params的类型具有使其成为{n1: number, n2: number}的子类型的结构,因此允许分配。

奇怪的规则(至少就上述原则而言)是对象文字的多余属性规则。这忽略了子类型对基本类型的可分配性,而倾向于捕获特定类别的错误。但是,此多余的属性检查仅适用于将对象文字直接分配给特定类型的引用的情况。如果您间接分配,则params的类型是从对象文字推断出来的,我们回退到可分配给基本类型规则的子类型。

虽然打字稿没有严格类型的概念,但是我们可以使用泛型函数以及映射和条件类型来模仿函数参数的这种效果:

let params = {n1: 100, n2: 200, foo: "x"};

interface IAddParms {n1: number, n2: number }
function add<T extends IAddParms>(p: T & Record<Exclude<keyof T, keyof IAddParms>, undefined>) {
    return p.n1 + p.n2
}
add({n1: 100, n2: 200, foo: "x"});  // error
add(params) // error
add({n1: 100, n2: 200 }) // ok

答案 1 :(得分:1)

打字稿中有某些事情会触发“多余的属性检查”,而某些事情却不会,这就是其中一种。

Typescript本质上是结构化的类型,这意味着上面的代码不太可能真正导致错误,因为只要它是具有这两个键的对象,它有多少个多余的键都无关紧要,因此这是一个权衡的打字机团队。

您可以在这里更深入地了解它: https://www.typescriptlang.org/docs/handbook/interfaces.html#excess-property-checks

如果您愿意,可以像这样永久打开多余的财产检查。 Forcing excess-property checking on variable passed to TypeScript function

希望这有助于让我知道。