TS7053

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

Broken Code ❌

interface Person {
  name: string;
}
 
function getValue(person: Person, key: string): string {
  return person[key];
}

The problem with the code listed above is that the parameter key can be any string but the Person interface only allows keys that relate to the properties of the Person interface (such as name).

Solution 1 ✔️

You can define an index signature for the Person interface which will allow all strings:

interface Person {
  [index: string]: string;
  name: string;
}
 
function getValue(person: Person, key: string): string {
  return person[key];
}

However, this is not recommend as it will allow you to access keys that are not defined (like age):

interface Person {
  [index: string]: string;
  name: string;
}
 
function getValue(person: Person, key: string): string {
  return person[key];
}
 
console.log(getValue({ name: 'Benny' }, 'age')); // returns `undefined`

Solution 2 ✔️

The better solution is using the keyof type operator which creates a literal string union for the possible keys of Person:

interface Person {
  name: string;
}
 
function getValue(person: Person, key: keyof Person): string {
  return person[key];
}

Solution 3 ✔️

You can also use type narrowing, to ensure that the key parameter is set to a string literal that is supported by the Person interface:

interface Person {
  name: string;
}
 
function getValue(person: Person, key: string): string {
  if (key === 'name') {
    return person[key];
  }
  return '';
}

Solution 4 ✔️

It is overly complex for this example, but another approach is to create an assertion function that ensures the key corresponds to a specific string literal that matches the Person interface:

interface Person {
  name: string;
}
 
function assertName(key: string): asserts key is 'name' {
  if (key !== 'name') {
    throw new Error(`Key is not "name".`);
  }
}
 
function getValue(person: Person, key: string): string {
  assertName(key);
  return person[key];
}

Solution 5 ✔️

Also overengineered, but you could solve the problem with a custom type guard:

interface Person {
  name: string;
}
 
function isName(key: string): key is 'name' {
  return key === 'name';
}
 
function getValue(person: Person, key: string): string {
  return isName(key) ? person[key] : '';
}