jossefaz
jossefaz

Reputation: 3932

Does typescript allow calling keys of interface dynamically

I have a interface :

interface MyInterface {
   field_1 : string
   field_2 : string
   // ... other fields with a certain number
}

And I have a getter function that receives an instance of MyInterface and should retrieve a value of an attribute of this object dynamically

const myFactory = (myInstance:MyInterface):string => { 
    // Here some logic to retrieve a specific number 
    // for convenience lets say that the output of this logic is 1
    let output = 1
    return myInstance[`field_${output}`] // <=== This does not work since it calls a property of the interface dynmically
}

How can I call a property of an interface dynamically ?

I saw the keyof operator : but I am not sure how to use it here

Upvotes: 2

Views: 68

Answers (2)

Gabriel
Gabriel

Reputation: 1449

The as keyword can only fool TypeScript into letting you do what you think you are doing.

With TypeScript 3 this might be the only way.

If you are using TypeScript 4+, you can enforce some validation on the resulting fieldKey, by using a Template Literal Type:

type MyField = `field_${number}`;
type MyType = Record<MyField, string>;

const myFactory = (instance: MyType): string => {
    let n = 12;
    const fieldName: keyof MyType = `field_${n}`;
    return instance[fieldName];
}

In this snippet, if you had given a non-number value to n, (such as "x") then it would yield to "field_x" which is rejected (i.e. detected as improper, by TypeScript at compile-time).

Same as if you write :

const fieldName: keyof MyType = `wrong_${n}`;

because wrong_12 is not valid in MyType.

If you need to build a larger MyType with additional fields not in the same shape as field_N, you could declare:

type LargerType = MyType & {
    anotherField: string;
};

Finally, if you need to limit the numbers, you could declare:

type MyField = `field_${1 | 2 | 3 | 4}`;

In this case, the snippet with n = 12 yields an error because 12 is already known to be forbidden. If the n value was obtained dynamically instead of inferred like here, it would need to be declared as:

let n: 1|2|3|4;

(or more likely, you would declare a type for the accepted numbers, since now you're using them in several places.)

Upvotes: 2

Danielle Zror
Danielle Zror

Reputation: 101

Option 1:

  type myKey = `field_${'1' | '2' | '3'}`

  const output = '3'
  const fieldKey: myKey = `field_${output}`

Option 2:

 const fieldKey = `field_${output}` as keyof MyInterface
 return myInstance[fieldKey]

Upvotes: 3

Related Questions