TypeScript implicit 'any' type with computed property key (irrelevant code removed for demo purposes)

I am new to using TypeScript and I have this React component which has an object literal inside the component, which I'm then accessing using a computed property key. Everything works as expected but I was getting the following TS error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ keyword: string; hex: string; rgb: string; }'.
No index signature with a parameter of type 'string' was found on type '{ keyword: string; hex: string; rgb: string; }'.ts(7053)


I managed to get rid of the error by adding the : any to the formats object, though I think this is not the best solution?

interface Props {
  keyword: string;
  hex: string;
  rgb: string;
  copyFormat: string;
}

export default ({ keyword, hex, rgb, copyFormat }: Props) =&gt; {
  const formats: any = {
    keyword,
    hex: hex.toUpperCase(),
    rgb: `rgb(${rgb})`,
  };

  return &lt;p&gt;{formats[copyFormat]}&lt;/p&gt;;
};
You very rarely need to use any in TypeScript because its type-system is so expressive.

The problem is that copyFormat is specified to be any string.

What would happen if you passed "blahblahblah" as copyFormat? Your types currently allow this!. Then, your code would attempt to do formats["blahblahblah"], but .blahblahblah is not a member of  the formats object. 

You need a more precise type for copyFormat. You intended the caller to only pass one of "keyword", "hex", or "rgb". You can make a type that is just those three values like so:

interface Props {
  keyword: string;
  hex: string;
  rgb: string;
  copyFormat: "keyword" | "hex" | "rgb";
}


It looks like Props really holds two things; it holds the actual data, and then a separate argument controlling what you read from it. It might make sense to split it into two arguments:

export default ({ keyword, hex, rgb }: Props, copyFormat: "keyword" | "hex" | "rgb") =&gt; {
  const formats = {
    keyword,
    hex: hex.toUpperCase(),
    rgb: `rgb(${rgb})`,
  };

  return &lt;p&gt;{formats[copyFormat]}&lt;/p&gt;;
}


If you don't like typing out all of the possible key options, you could instead use the keyof type operator to say that copyFormat is any valid key in the Props type. TypeScript will see that all of the properties are strings and correctly infer the resulting type of formats[copyFormat] is thus a string.

interface Props {
  keyword: string;
  hex: string;
  rgb: string;
}

export default ({ keyword, hex, rgb }: Props, copyFormat: keyof Props) =&gt; {
  const formats = {
    keyword,
    hex: hex.toUpperCase(),
    rgb: `rgb(${rgb})`,
  };

  return &lt;p&gt;{formats[copyFormat]}&lt;/p&gt;;
};
```