简体   繁体   English

TypeScript如何处理接口和类中的多余属性的区别

[英]Difference in how TypeScript handles excess properties in interfaces and classes

I have recently stumbled upon this weird (imo) behavior in TypeScript. 我最近偶然发现了TypeScript中这种奇怪的(imo)行为。 During compilation it will complain about excess properties only if the expected variable's type is an interface if the interface has no mandatory fields. 在编译期间,只有当接口没有必填字段时,如果预期变量的类型是接口,它才会抱怨多余的属性。 Link to TypeScript Playground #1: http://goo.gl/rnsLjd 链接到TypeScript Playground#1: http//goo.gl/rnsLjd

interface IAnimal {
    name?: string;  
}

class Animal implements IAnimal { 

}

var x : IAnimal = { bar: true }; // Object literal may only specify known properties, and 'bar' does not exist in type 'IAnimal'
var y : Animal = { bar: true }; // Just fine.. why?

function foo<T>(t: T) { 

}

foo<IAnimal>({ bar: true }); // Object literal may only specify known properties, and 'bar' does not exist in type 'IAnimal'
foo<Animal>({ bar: true }); // Just fine.. why?

Now, if you add a 'mandatory' field to the IAnimal interface and implement it in the Animal class it will start complaining about 'bar' being an excess property for bot interfaces and classes. 现在,如果你向IAnimal接口添加一个'强制'字段并在Animal类中实现它,它将开始抱怨'bar'是bot接口和类的多余属性。 Link to TypeScript Playground #2: http://goo.gl/9wEKvp 链接到TypeScript Playground#2: http//goo.gl/9wEKvp

interface IAnimal {
    name?: string;  
    mandatory: number;
}

class Animal implements IAnimal {
    mandatory: number;
}

var x : IAnimal = { mandatory: 0, bar: true }; // Object literal may only specify known properties, and 'bar' does not exist in type 'IAnimal'
var y : Animal = { mandatory: 0, bar: true }; // Not fine anymore.. why? Object literal may only specify known properties, and 'bar' does not exist in type 'Animal'

function foo<T>(t: T) { 

}

foo<IAnimal>({ mandatory: 0, bar: true }); // Object literal may only specify known properties, and 'bar' does not exist in type 'IAnimal'
foo<Animal>({ mandatory: 0,bar: true }); // Not fine anymore.. why? Object literal may only specify known properties, and 'bar' does not exist in type 'Animal'

If anyone has some insights as to why that works as it does please do. 如果有人有一些见解,为什么它的工作原理,请做。
I am very curious as to why that is. 我很好奇为什么会这样。

The following three bullet points from pull request shed a bit of light on the new strict behavior in TS 1.6 which is used in the playground: 来自pull请求的以下三个要点揭示了在操场上使用的TS 1.6中的新严格行为:

  • Every object literal is initially considered "fresh". 每个对象文字最初都被认为是“新鲜的”。
  • When a fresh object literal is assigned to a variable or passed for a parameter of a non-empty target type [emphasis added], it is an error for the object literal to specify properties that don't exist in the target type. 将新对象文字分配给变量或为非空目标类型的参数传递[强调添加]时,对象文字指定目标类型中不存在的属性是错误的。
  • Freshness disappears in a type assertion or when the type of an object literal is widened. 新鲜度在类型断言中消失,或者当对象文字的类型变宽时消失。

I have found in the source code function hasExcessProperties and function isKnownProperty with the comment: 我在源代码function hasExcessProperties找到了function hasExcessPropertiesfunction isKnownProperty的注释:

 // Check if a property with the given name is known anywhere in the given type. In an object type, a property // is considered known if the object type is empty and the check is for assignability, if the object type has // index signatures, or if the property is actually declared in the object type. In a union or intersection // type, a property is considered known if it is known in any constituent type. function isKnownProperty(type: Type, name: string): boolean { if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); if (relation === assignableRelation && (type === globalObjectType || resolved.properties.length === 0) || resolved.stringIndexType || resolved.numberIndexType || getPropertyOfType(type, name)) { return true; } } else if (type.flags & TypeFlags.UnionOrIntersection) { for (const t of (<UnionOrIntersectionType>type).types) { if (isKnownProperty(t, name)) { return true; } } } return false; } 

So target type Animal (the class) in your first example is an empty type - it has no properties because you did not implement name property in the class (therefore resolved.properties.length === 0 is true in isKnownProperty function). 因此,第一个示例中的目标类型Animal (类)是一个空类型 - 它没有属性,因为您没有在类中实现name属性(因此在isKnownProperty函数中resolved.properties.length === 0为true)。 On the other hand IAnimal has properties defined. 另一方面, IAnimal具有定义的属性。

I may have described the behavior a bit technically but ... hopefully, I made it clear and hopefully, I did not make a mistake along the road. 我可能已经在技术上描述了这种行为,但是...希望,我明确表示并希望,我没有在路上犯错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM