The difference is how TypeScript handles redundant properties in interfaces and classes.

I recently came across this weird behavior (imo) in TypeScript. At compile time, it will complain about redundant properties only if the expected type of the variable is an interface, if the interface does not have required fields. Link to TypeScript Playground No. 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 “required” field to the IAnimal interface and implement it in the Animal class, it will start complaining about the “bar”, which is a redundant property for bot interfaces and classes. Link to TypeScript Playground No. 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'

- , , , . , .

+4
1

pull request TS 1.6, :

  • "".
  • [ ], , , t .
  • .

function hasExcessProperties function 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;
 }

, Animal () - , name ( resolved.properties.length === 0 true isKnownProperty)). , IAnimal .

, , ... , , .

+2

Source: https://habr.com/ru/post/1615085/


All Articles