I'm using the latest Typescript version :2.6.2
.
I'm encountering a bizarre situation where if I do foo({a:1,b:2})
- things don't work whereas if I do : foo({b:2,a:1})
- they do work.
I have a generic class , an interface which has 2 properties and a function.
Here is the code :
class MyClass<T> {
value: T;
next(value: T): void {
}
}
export enum StateKey { backlogItems='backlogItems'}
export interface State {
backlogItems : number[];
[key: string]: any
}
class A {
private subj = new MyClass<State>();
public set<T>(name: StateKey, state: T) {
this.subj.next({ backlogItems: [...this.subj.value.backlogItems] ,
[name]:state //<--- error here
})
}
}
I get an error :
Argument of type '{ [name]: T; }' is not assignable to parameter of type 'State'. Types of property 'backlogItems' are incompatible. Type 'T' is not assignable to type 'number[]'.
But If I change the order of the literals in the object :
from :
this.subj.next({ backlogItems: [...this.subj.value.backlogItems], [name]: state })
to :
this.subj.next({ [name]:state, backlogItems: [...this.subj.value.backlogItems] })
Question
Why does changing the order makes it compile ?
Image proof
Typescript play ground demo
It compiles without errors if you change the string in StateKey
enum to some other value, like this
export enum StateKey { backlogItems='somethingElse'}
(the error also goes away if you add more values to the enum)
The compiler is doing type inference and detects that [name]
in your argument to next()
function can be only backlogItems
, which is declared as number[]
.
So both variants of the object literal are assigning two different values to the same property, that's why order of assignment matters - the last one wins, basically. Compiler reports the error in case when the last assigned value is incompatible with declared property type - T
is not compatible with number[]
.
UPDATE
Also, to preserve the code and just get rid of the error, you can make the compiler forget about the type it inferred for name
by assigning it to the intermediate variable with string
type:
class MyClass<T> {
value: T;
next(value: T): void {
}
}
export enum StateKey { backlogItems='backlogItems'}
export interface State {
backlogItems : number[];
[key: string]: any
}
class A {
private subj = new MyClass<State>();
public set<T>(name: StateKey, state: T) {
const key: string = name;
this.subj.next({ backlogItems: [...this.subj.value.backlogItems] ,
[key]:state
})
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With