1

I have the following code:

interface MyType {
    a: string;
    b: number;
}

class SomeClass {
    // ... more things going on

    beta: MyType = {a: 'Lorem', b: 3};

    myFunction(alpha: MyType) {
        // beta is an object of type MyType
        for (const key in alpha) {
            this.beta[key as keyof typeof this.beta] = alpha[key as keyof typeof alpha] ?? '';
        }
    }
}

see identical playground here.

However, this produces the error:

Element implicitly has an 'any' type because expression of type 'string | number | symbol' can't be used to index type 'MyType'.
  No index signature with a parameter of type 'string' was found on type 'MyType'.

I've tried playing around with it for a long time, tweaking it, and it still doesn't work -- I've no idea why. (I'm using keyof typeof this.beta for simplicity but keyof MyType doesn't work either).

Thanks for the help in advance.

1
  • In case the error message isn't clear, the compiler is complaining because it sees key as a string (since the in operator is used) and it cannot find a type indexer on MyType (e.g. { [k: string]: any }). I don't believe the compiler is smart enough to infer that a string like 'a' corresponds to the a property, which has a string value type. Ultimately, I can't see a solution at all with the in loop approach here. You could consider Object.assign instead. Commented Jul 26, 2021 at 3:15

1 Answer 1

1

I believe that this question is very similar to yours.

Consider this example:

interface MyType {
    a: string;
    b: number;
}

class SomeClass {
    beta: MyType = { a: 'Lorem', b: 3 };

    myFunction(alpha: MyType) {
        // beta is an object of type MyType
        for (const key in alpha) {
            const x = this.beta[key as keyof MyType]   // string | number
            const y = alpha[key as keyof typeof alpha] // number | number

            this.beta[key as keyof MyType] = alpha[key as keyof typeof alpha] ?? ''; // expected error
        }
    }
}

As you see, x and y can be a string or number.

declare var x: string | number;
x = 42;
x = 'str'

Since TS is about static type checking, it is unable to figure out whether this.beta[key] has same type as alpha[keys], they both are string | number.

You are getting Type 'string | number' is not assignable to type 'never'

This is because objects are contravariant in their key types. And candidates for the same type variable in contra-variant positions causes an intersection type to be inferred. Hence string & number gives never.

My advise - don't mutate values in typescript.

You can find more explanations and examples in my blog

How to fix it?

interface MyType {
    a: string;
    b: number;
}

class SomeClass {
    beta: MyType = { a: 'Lorem', b: 3 };

    myFunction(alpha: MyType) {
        const result = (Object.keys(alpha) as Array<keyof MyType>).reduce<MyType>((acc, elem) => ({
            ...acc,
            [elem]: alpha[elem]
        }), this.beta)

        this.beta = result
    }
}

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.