doberkofler
doberkofler

Reputation: 10351

How to access a property of an object using the bracket notation in TypeScript strict mode

The following TypeScript sample code shows an error Element implicitly has an 'any' type because type '{one: number; two: number;}' has no index signature in the line const one = obj[prop]; in strict mode.

The compiler allows the line const two = obj[propName];, so I cannot understand why the error is shown or how to generally speaking access a property of an object using the bracket notation.

const obj = { one: 1, two: 2 };

const props = { one: 'one', two: 'two' };

// it is not possible add or change any properties in the props object 
props.zero = 'zero';
props.one = 1;

// prop has the type string
const prop = props.one;

// using the bracket notation fails with the following error message:
// Element implicitly has an 'any' type because type '{one: number; two: number;}' has no index signature.
// const prop: string
const one = obj[prop];

// this works because propName is of type 'two'
const propName = 'two';
const two = obj[propName];

Upvotes: 7

Views: 12849

Answers (3)

Vectorjohn
Vectorjohn

Reputation: 411

As has been mentioned, the inferred type of props is {one: string, two: string}. This means props.one could be any string (even though we see it is "one"), so when you write obj[props.one] what you're really accessing is obj[string] which may not be allowed.

If you made your props an explicit type, you could get around it: type Props = {one: "one", two: "two"}, and then use it: const props: Props = {one: "one", two: "two"}

This way the type of props is not inferred, and the explicit type of the values is "one" or "two".

But that's annoying to write when what you really want to say is that props is these exact values. You can tell the compiler not to be clever about the type it infers and use the exact values you specified with as const:

const obj = { one: 1, two: 2 };
const props = { one: 'one', two: 'two' } as const;

const prop = props.one;

const one = obj[prop];

Now the compiler knows that prop has the exact value "one" instead of any string. So it is allowed.

Upvotes: 0

Agustin Eloy Barrios
Agustin Eloy Barrios

Reputation: 128

Particularly I'm fan of strong type, but const obj: any should be ok.


interface test { 
    one: string;
    two: string;
}

const props: test = { one: 'one', two: 'two' }; // {one: string, two: string}
const obj: any = {one: 1, two: 2}; // {one: number, two: number}


const two = obj['two']; // number

const prop = props.one; // string

const one = obj[prop];

Enum Solution

enum numbers {
    one = 1,
    two = 2
}

const oneStr = numbers[numbers.one];
const one = numbers[oneStr];

Upvotes: -1

Christoph Lütjen
Christoph Lütjen

Reputation: 5804

Element implicitly has an 'any' type because type '{one: number; two: number;}' has no index signature

Your object has no index signature, it has two named properties.

The compiler allows the line const two = obj['two'];

It allows the names of your properties, that's why obj['two'] and obj['one'] will work.

and the const prop is a string, so I cannot understand why the error is shown

Because a string can have much more values that just 'one' or 'two' and so the compiler cannot ensure, your object[myString] call will work. It's only valid for two defined string values.

how to generally speaking access a property of an object using the bracket notation.

This would work:

 const prop: 'one' | 'two' = 'one';
 const test = obj[prop]

You say: prop has value 'one' or 'two' and so the compiler knows your obj[prop] will always be valid.

or

 class Example {
   one: string;
   two: string;
 }
 const prop: keyof Example = 'one';
 const test = obj[prop];

Here keyof(ClassName) tells the compiler, that your prop var will have an existing property name of Example.

The above examples assume, that you have an object where only the properties named 'one' and 'two' are valid. If you want to use your object in a "dictionary style", tell typescript about that and add an index signature:

 const obj: {[key:string]: string;} = { one: 'one' };
 const text = obj[myString];

Now obj allows every string values as key.

Upvotes: 14

Related Questions