angulartypescript

Can't access a field of an object in TypeScript by using a variable as a name of the index unless declared as a constant


I can't understand why the first two printouts work, while the other two produce an error.

const index1 = "name";
const holder: { field: string; } = { field: "name" };
const index2 = holder.field;

const target = { name: "beep" } as MyType;

console.log(target["name"]);
console.log(target[index1]);

console.log(target[holder.field]);
console.log(target[index2]);

The error says:

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

How can I get a field by a variable name coming from a non-constant?

I haven't found good answers addressing this specific issue in a way that I recognized. Most confusing is the fact that I am using a string to get the field in the object in both cases. The only difference is the const declaration.

I've tried adding as const according to an example without any difference.

The field value comes from this interface. It definitely is a string. In the sample above, I even simplified the type definition.

export interface SortInfo {
  field: string;
  direction: number;
}

The object itself that I'm trying to get the field from is declared by this interface.

export interface MyType {
  id: string;
  name: string;
  type: number;
  ...
}

Solution

  • TypeScript is telling that it cannot be sure that the key you are using is valid for this object.

    Just as target["foobar"] doesn't compile, so does target[key] if the type of key is string.

    You need the type of your key to be keyof typeof target, or in your case keyof MyType, which is "id" | "name" | "type" | ...

    You can define holder as { field: keyof MyType; }:

    const holder: { field: keyof MyType; } = { field: "name" };
    console.log(target[holder.field]); // This works fine
    

    If that type should really be SortInfo, then you can use

    export interface SortInfo {
      field: keyof MyType;
      direction: number;
    }
    

    And of course if MyType can be different things you'd use generics:

    export interface SortInfo<T> {
      field: keyof T;
      direction: number;
    }