5

Have a look at this example typescript code

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

printLabel({ size: 10, label: 'hello' });

The above code fails to compile with the following error:

1.ts:6:14 - error TS2345: Argument of type '{ size: number; label: string; }' is not assignable to parameter of type '{ label: string; }'. Object literal may only specify known properties, and 'size' does not exist in type '{ label: string; }'.

In short, size is an excess property and not conforming to the type { label: string } resulting in compiler yelling. Let's alter the above code snippet a little:

function printLabel(labelledObj: { label: string }) {
    console.log(labelledObj.label);
}
const obj = { size: 10, label: 'hello' }
printLabel(obj);

Now we extracted the object literal which was passed to printLabel in earlier example into an intermediary reference named obj, the weird part is that now it does not complain and works perfectly. Why does typescript behaves so?

Muhammad Saqib
  • 1,037
  • 3
  • 10
  • 16

2 Answers2

13

It's by design. In short, Typescript creators made it this way because they know Javascript is a very dynamic language with many such use cases.

You should read this carefully: https://www.typescriptlang.org/docs/handbook/interfaces.html#excess-property-checks (however I bet the question arised from reading it).

Object literals get special treatment

Their logic might be like this: if you have a variable, then it may come from some third party and there is not much you can do with it. On the other hand, if you pass an object literal, then you are responsible for its correct type.

Nurbol Alpysbayev
  • 19,522
  • 3
  • 54
  • 89
  • can you please cite the reasons behind this design decision? – Muhammad Saqib Oct 17 '18 at 10:12
  • I updated my answer slightly. But the main reason is that TypeScript has to do many such "silly" design decisions because it has to be compatible with JS world. Another such decision is structural subtyping, for instance. – Nurbol Alpysbayev Oct 17 '18 at 10:16
  • Note that the link above leads to the deprecated handbook v1, and there is apparently no piece of the v2 handbook that mentions this ‍♂️; you might want to link to [the section of the TS1.6 release notes that introduces the feature](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-6.html#stricter-object-literal-assignment-checks) instead. – jcalz Jan 24 '23 at 03:40
1

If you write an object literal with excess properties for the type that object literal is being assigned to, then the excess properties cannot possibly be accessed in a type-safe way - there is only one reference to the object (passed whereever the object literal is used), and that reference has a less-specific type which doesn't know about the excess properties. So those extra properties are inaccessible by sensibly-written code, and this means you probably made a mistake.

When the object is first referenced by a variable, and the variable's type is inferred from the object, that variable's type is specific, so those extra properties can still be accessed via that variable in a type-safe way. So the extra properties are not inaccessible and there is no evidence you made a mistake.

kaya3
  • 47,440
  • 4
  • 68
  • 97