Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript breaking change with pushing and assigning a 'derived' value into a 'base' array

Tags:

typescript

I haven't been able to locate the specific breaking change.

Q: may anyone explain the reason why this behaviour shown now gives a 'Object literal may only specific known properties' error since TS 5+?

Playground: https://www.typescriptlang.org/play/?ts=5.0.4#code/JYOwLgpgTgZghgYwgAgEJwM4oN4FgBQyywAJgFzIgCuAtgEbQDcBAvgQaJLIigCLTAAbhBLIIAD0ggSGNJhwEiIODQgUMYKKADmzfG3wEEAexAbikGhgrosAbQC6yALzJHeggBsIYCxBoAjBT8WsIket6+wJYATMECYR740f4BLsjYxOTIAQA0lCpqyADkYAAWwLKVyMYA1shQENpwUCTeGBjFyCx6KVYAdAAOVBhlABR9AQCUSX0YQyPjE7HpmaQUMfnKqhSlFVWyAHIA8gAqNfWgyACs-QAM-QAs+XRUUbJ1xCDIj-0AnP1rl0WFMZkA

Code:

interface Base {
  id: number;
}

interface Derived extends Base {
  name: string;
}

const items: Base[] = [];

let item1: Derived;
let item2: Derived;

item1 = { id: 1, name: 'this is ok regardless' };
items.push(item1);

items.push((item2 = { id: 2, name: 'this is NOT ok in 5.0.4, but is ok in 4.9.5' }));
like image 265
Adam Marshall Avatar asked Jan 22 '26 12:01

Adam Marshall


1 Answers

This change occurred between 4.9.5 and 5.0.2 and it is specifically related to how contextual types are cached. [github.com] [1]


Background

Assignments return the value of their right hand side. The assignment x = y returns y.

In the case below: [2]

item = { id: 2, name: 'this is NOT ok in 5.0.4, but is ok in 4.9.5' }

The return value of the assignment is the object literal:

{ id: 2, name: 'this is NOT ok in 5.0.4, but is ok in 4.9.5' }

During this assignment, the object literal is contextually inferred to be of type Derived.

Pre 4.9.5 and 4.9.5, this contextual type is cached but post 4.9.5, it no longer is.


What this means is that pre 4.9.5, after the assignment, the type of the object literal returned from the assignment is Derived. This type is cached and is assignable to Base but post 4.9.5, the caching is not done so the type of the object literal is rechecked to use in the assignment to the parameter of type Base. After rechecking, excess property check throws an error.


A simple fix would be type asserting the object literal before assignment or type asserting the result of the assignment (i.e. type asserting the object literal after assignment):

item = { id: 2, name: 'this is NOT ok in 5.0.4, but is ok in 4.9.5' } as Derived

OR

(item = { id: 2, name: 'this is NOT ok in 5.0.4, but is ok in 4.9.5' }) as Derived

This will prevent any form of contextual type inference on the object literal.


[1] In my humble opinion, it isn’t a major breaking change, that is probably why it wasn’t documented.

[2] The case uses a contrived example which is emulated from the example in the OP but not exact. This is for brevity.

like image 174
Chukwujiobi Canon Avatar answered Jan 25 '26 19:01

Chukwujiobi Canon