I got this error:

Type '{ [key: string]: any; }' is not assignable to type 'T'.
  '{ [key: string]: any; }' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ [key: string]: any; }'.(2322)

from this piece of code:

function getValue ():{[key: string]: any}  {
    return {key:'value'}
}

class Foo<T extends {[key: string]: any}> {
    public readonly data?: T

    constructor() {
        this.data = getValue()
    }
}

Does anyone know why and how to solve this error?

2

There are 2 best solutions below

0
On

Are you looking to store a dictionary of items with type T? Then maybe this is what you want?:

function getValue ():{[key: string]: any}  {
    return {key:'value'}
}

class Foo<T> {
    public readonly data?: {[key: string]: T}

    constructor() {
        this.data = getValue()
    }
}
0
On

The compiler complains that you did not establish a direct relationship between the return type of the getValue function and the data instance property. extends clause only guarantees that at a minimum the generic type parameter is assignable to the provided constraint, but does not bound it otherwise.

Moreover, your getValue function returns a constant of type { key : 'value' }. Therefore, when you assign the return type of a call to getValue to this.data, the compiler looks if the latter is a supertype of the former and sees that you only guaranteed data to be { [key: string]: any }, or, in plain English:

"some kind of object with any number of keys of type string and values of any type"

It should be obvious that data can have nothing in common with the { key : 'value' } type. Now, take a look at what happens if you explicitly tell the compiler that T should conform to the return type of the getValue instead:

class Foo<T extends {[key: string]: any}> {
    public readonly data?: T | ReturnType<typeof getValue>;

    constructor() {
        this.data = getValue(); //OK
    }
}

Now the compiler is happy because it can establish the relationship, but you become limited to objects with single key key and values of type string. Frankly, from your snippet, it is unclear why do you need to make the class generic at all:

class Foo {
    public readonly data?: ReturnType<typeof getValue>;

    constructor() {
        this.data = getValue(); //OK
    }
}