Home > Back-end >  typescript - access to object properties through indexer - obj[key]
typescript - access to object properties through indexer - obj[key]

Time:10-12

i need to access to an object property using a string as key

interface MyObject {
  prop1: string;
  prop2: string;
  prop3: string;
  prop4: string;
  prop5: string;
} 

let initialValues: MyObject;

//i set some property
initialValues = {
    prop1: 'xxxx'
    prop2: 'yyyy'
}

//i set some other (existing) property 
[3,4,5,6].map(i => {
    initialValues[`prop${i}`] = 'zzzz';
});

i got this error

Element implicitly has an 'any' type because expression of type 'any' can't be used to index type 'MyObject'

CodePudding user response:

Typescript has assumed widest possible type for [3,4,5,6] as string[]. Add as const to narrow it.

([3,4,5] as const).map(i => {
initialValues[`prop${i}`] = 'zzzz';});

Typescript playground

CodePudding user response:

Typescript will not be able to understand that your values will be valid key of the interface. It is a limitation.

Best you can do is use type assertion, but do not that it does not provide type safety instead you are telling TS that the type is correct :

interface MyObject {
  prop1: string;
  prop2: string;
  prop3?: string;
  prop4?: string;
  prop5?: string;
} 

let initialValues: MyObject = {
  prop1: 'x',
  prop2: 'y'
}

let ans = [3,4,5,6].map(i => {
    initialValues[`prop${i}` as keyof MyObject] = 'zzzz';
});

Notice TS cannot figure out that 6 will not be a problem here.

Link

CodePudding user response:

Use a type assertion to tell TypeScript that this will be a property of the object:

interface MyObject {
  prop1: string;
  prop2: string;
  prop3: string;
  prop4: string;
  prop5: string;
} 

let initialValues: MyObject;

//i set some property
initialValues = {
    prop1: 'xxxx'
    prop2: 'yyyy'
}

//i set some other (existing) property 
[3,4,5,6].map(i => {
    initialValues[`prop${i}` as keyof MyObject] = 'zzzz';
});

TypeScript Playground

However, be careful when you do this! TypeScript won't check that the property is a valid property of the object, so you lose complete runtime type safety. You can see that in this case TypeScript doesn't complain.

Alternatively, if you know which values will be in the array, you can tell TypeScript that, and it will be able to recognize valid keys:

interface MyObject {
  prop1: string;
  prop2: string;
  prop3?: string;
  prop4?: string;
  prop5?: string;
} 

let initialValues: MyObject;

//i set some property
initialValues = {
    prop1: 'xxxx',
    prop2: 'yyyy'
};

//i set some other (existing) property 
let values: (3 | 4 | 5 | 6)[] = [3, 4, 5, 6];
values.map(i => {
    initialValues[`prop${i}`] = 'zzzz';
});

TypeScript Playground

You can see that in this case, TypeScript only complains about prop6. This is the preferred solution if you can do it, because we keep type safety.

CodePudding user response:

When you want to init some properties, you could use an init function that accepts a partial type and returns MyObject.

interface MyObject {
  prop1: string;
  prop2: string;
  prop3: string;
  prop4: string;
  prop5: string;
} 

function init(partial: Partial<MyObject>): MyObject {
  const result: any = {...partial};

  [3,4,5].map(i => {
    result[`prop${i}`] = 'zzzz';
  });
  return result;
}

const initializedObj = init({
    prop1: 'xxxx',
    prop2: 'yyyy'
});

console.log(initializedObj);

Playground Example

  • the type of the init function will make sure, that you only pass an allowed object
  • But of course you must be very careful that you really return an object that is a valid MyObject: i.e. in your question you also fill in prop6 which does not exist on MyObject
  • Related