TypeScrip中的对象类型是open和extendible,而不是closed、sealed或"exact"(如特征请求microsoft/TypeScript#12936中所描述的).允许对象类型的值具有该类型中未提及的额外属性.如果weren't是这种情况,则extending个接口或类将 destruct 类型兼容性:
interface Foo { bar: string; }
interface Baz extends Foo { qux: number; }
const baz: Baz = { bar: "abc", qux: 123 };
const foo: Foo = baz; // okay
有一个名为excess property checking的功能,如果您将具有额外属性的object literal赋给不需要它们的东西,它会发出警告:
const fooDirect: Foo = { bar: "abc", qux: 123 }; // error!
// --------------------------------> ~~~~~~~~
// Object literal may only specify known properties, and
// 'qux' does not exist in type 'Foo'.
这并不是因为赋值是invalid(至少不是根据 struct 类型系统);毕竟,允许使用等价的赋值const foo: Foo = baz;
.之所以会出现警告,是因为在分配之后,对qux
属性的了解完全丢失了,这可能是一个错误.与const foo: Foo = baz;
相比,baz
之后仍然是可访问的,因此知识仍然存在.
因此,多余的属性警告不是类型安全问题;它们更像是Linter特性.这可能是一个有用的功能,但不幸的是,它会引起很多困惑,有时我认为它可能会带来更多麻烦,而不是它的价值.它让人们认为类型脚本对象类型是准确的,而事实并非如此,然后当人们通过为中间变量赋值来绕过该功能时,或者当使用不强制额外属性警告的语言的一部分时,比如复制到中间变量,或者像这个问题中使用带有可选判别式的区分联合时发生的情况,人们就会抱怨.
这意味着这里的任务:
type P =
{ foo: boolean, bar?: undefined } |
{ foo: boolean, bar: boolean, baz: boolean };
const p: P = { foo: true, baz: true };
将是valid,无论你是否收到过多的财产警告.类型系统中没有不能将{foo: true, baz: true}
赋值到{foo: boolean, bar?: undefined}
的内容.如果希望赋值为invalid,则需要更改P
以实际禁止baz
键.好吧,TypeScrip不能真正禁止键,但它确实支持the impossible never
type中的optional properties,因此属性要么缺失,要么是undefined
,这是相似的.就像这样:
type P =
{ foo: boolean, bar?: undefined, baz?: never} |
{ foo: boolean, bar: boolean, baz: boolean };
const p: P = { foo: true, baz: true }; // error
这为您提供了您想要的错误,即使您首先try 将其复制到某个中间变量,也应该是错误的……因为你不依赖于超额财产警告.
话虽如此,你在这里看到的行为被认为是打印脚本中的一个错误,据报道是microsoft/TypeScript#39050.当您提到受歧视unions 的相关成员中不存在的房产时,应该会出现过度房产警告.但是,当判别属性是可选的并且缺失时,似乎不会发生这种情况.因此,也许有一天,这种行为会改变为你想要的方式.
但同样要清楚的是,即使他们修复了这一点,这也只是一个可以很容易绕过的过度财产警告,正如你可以看到的那样,如果你把你的判别式改为必需的:
type P =
{ foo: boolean, bar: 0 } | { foo: boolean, bar: 1, baz: boolean };
const p: P = { foo: true, bar: 0, baz: true }; // error you wanted
// -----------------------------> ~~~~~~~~~
const indirect = { foo: true, bar: 0, baz: true } as const;
const pIndirect: P = indirect; // no error
因此,无论如何您都可能想要使用可选的-never
方法.
Playground link to code个