How type checking in Typescript works with passing argument?

660 Views Asked by At

With TypeScript, could anyone explain for me why the two function calls below give different type checking result?

interface LabelledValue {
    label: string;
}

function printLabel(labelledObj: LabelledValue) {
    console.log(labelledObj.label);
}

var myObj = {size: 10, label: "Size 10 Object"};

//This calls is OK

**printLabel(myObj);**

//But this call is warned with error "size doe snot exist in LabelledValue

**printLabel({ size: 10, label: "Size 10 Object" });**

Many thanks.

3

There are 3 best solutions below

0
On

Typescript allows certain un-sound operations that can’t be known at compile-time to be safe.

printLabel(myObj);  // un-sound operation

Quote from Typescript handbook:

The basic rule for TypeScript’s structural type system is that x is compatible with y if y has at least the same members as x

Here obj is type compatible with LabelledValue since obj has all the members LabelledValue require (i.e. label: string)

However the following gives a compile error since object literal can only specify known properties:

printLabel({ size: 10, label: "Size 10 Object" });  // ERROR

To correct this error, simply explicitly cast the object literal as <LabelledValue>:

printLabel(<LabeledValue>{ size: 10, label: "Size 10 Obect" });  // OK
3
On

In the first case, typescript implicitly casts an object of type { size: number, label: string } to LabelledValue, which works fine.

In the second case, typescript thinks you are trying to create an object of type LabelledValue, so it gets mad because size is not a valid property.

You would get the same error in the first case if you assigned a type to myObj, e.g.

var myObj: LabelledValue = {size: 10, label: "Size 10 Object"}; // causes compiler error

I don't know what you are trying to do with this, but one possible workaround would be to add size as an optional property of your interface, like this:

interface LabelledValue {
    label: string;
    size?: number;
}
0
On

This is a deliberate decision by authors of TypeScript. Having excess properties in object literal is likely a result of a typo, so it's diagnosed as error. Also, with code like the second call of printLabel in your question, size property is not going to be used anywhere, so there is no point in having it in object literal there.

Quote from the documentation:

However, TypeScript takes the stance that there’s probably a bug in this code. Object literals get special treatment and undergo excess property checking when assigning them to other variables, or passing them as arguments. If an object literal has any properties that the “target type” doesn’t have, you’ll get an error.