Reputation: 923
When defining interfaces, the TypeScript documentation mentions that as long as an object takes the shape of the interface any excess object properties are allowed.
An example
interface Person {
name: string
}
function print(somebody: Person) {
console.log(somebody.name);
}
let obj = { name: "Johnny", additionalProps: true }
print(obj); // this is okay
But is this only true for function parameters? Below I try to create an object cast as a specific type, and adding additional properties throws errors only when I don't use curly braces.
interface Animal {
name: string;
}
let myDog = <Animal> {
name: "Spot",
altProperty: "anything" // no error
};
myDog.altProperty = "anything else"; // Property 'altProperty' does not exist on type 'Animal'
It seems you can assign as many properties as you like to an object when asserting its type, but you cannot access any of these because they are not in the type definition. Why is this?
Upvotes: 0
Views: 669
Reputation: 1242
The reason is because TypeScript's type system is based on structural subtyping which I think can be summarized best by saying that an object corresponds to a type if it has the attributes of that type. Some people joke that it is just "duck typing" (i.e. if it quacks like a duck then it is a duck).
So in your example it actually has nothing to do with being declared with an interface vs simply curly braces.
interface Person {
name: string
}
interface PersonPlusMore {
name: string,
age: number
}
function print(somebody: Person) {
console.log(somebody.name);
}
let obj : PersonPlusMore = { name: "Johnny", age: 30 }; // <-- notice that we used explicit typing
print(obj); // this is still okay because it has "at least" the properties that your print function requires
In this manner, TypeScript keeps the flexibility of JavaScript but still makes sure that functions have the data that they require to process the information properly at runtime.
Upvotes: 0
Reputation: 27367
Interfaces in typescript merely provide compile-time checks explaining what members are available on the object.
Your code here:
let myDog = <Animal>
Is saying "I have some object, but I want to only expose the members defined by the Animal
interface". You've explicitly told the compiler to give you an error when ever you reference a member not defined in Animal
.
You're able to reference altProperty
when creating the object, because you haven't given it a type yet. However, were you to write:
let myDog: Animal = {
//name: "Spot",
altProperty: "anything" // no error
};
You would get an error for attempting to cast an invalid object to Animal
Now, you don't need to cast the object to Animal
to be able to use it as such. You could write:
interface Animal {
name: string;
}
let myDog = {
name: "Spot",
altProperty: "anything"
};
myDog.altProperty = "anything else";
doSomething(myDog);
function doSomething(object: Animal) {}
And it would work fine. In fact, the only reason to explicitly type a variable like you've done would be to deliberately catch the error you're experiencing.
Upvotes: 1