user8380672
user8380672

Reputation: 720

No index signature with a parameter of type 'string' was found on type ts(7053)

I keep getting the below error from this line of code: oldUniforms[key] = tempUniforms[key];. I don't understand why this doesn't work as I'm just creating a deep copy of an object of the same type.

Error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'UpdatedUniformsType'.
  No index signature with a parameter of type 'string' was found on type 'UpdatedUniformsType'.ts(7053)

Code:

    type UpdatedUniformsType = {
        u_BlockDia?: number;
        u_CamPos?: [number, number];
    };

    const oldUniforms: UpdatedUniformsType = {
        u_BlockDia: 16,
        u_CamPos: [0, 0],
    };

    function setOldUniforms(updatedUniforms: UpdatedUniformsType) {
        // Deep copy of updatedUniforms
        const tempUniforms: UpdatedUniformsType = JSON.parse(JSON.stringify(updatedUniforms));

        // Update oldUniforms using the deep copy
        Object.keys(tempUniforms).forEach((key) => {
            oldUniforms[key] = tempUniforms[key];
        });
    }

Upvotes: 0

Views: 217

Answers (1)

jcalz
jcalz

Reputation: 330266

The compiler does not know that Object.keys(tempUniforms) will produce only the expected keys. Object type in TypeScript are open/extendible and not closed/exact. If I have an object of type UpdatedUniformsType I can talk meaningfully about what's happening at the u_BlockDia and u_CamPos keys, but I have no idea if it will have additional properties or not. So Object.keys(obj) returns a string[] in TypeScript, and not an Array<keyof typeof obj>. See this question and its answer for more information.

If you're sure that there won't be extra keys (or if you don't care what happens when there are extra keys), then you can use a type assertion to tell the compiler Object.keys(tempUniforms) will return Array<keyof UpdatedUniformsType>:

(Object.keys(tempUniforms) as Array<keyof UpdatedUniformsType>)
    .forEach(<K extends keyof UpdatedUniformsType>(key: K) => {
        oldUniforms[key] = tempUniforms[key];
    });

I also had to make the forEach() callback a generic function so that the assignment oldUniforms[key] = tempUniforms[key] is seen by the compiler as assigning UpdatedUniformsType[K] to itself. If you don't do that, the compiler doesn't realize that the assignment is safe, due to a soundness improvement introduced in TS3.5. See this question and its answer for more information.

Playground link to code

Upvotes: 1

Related Questions