Mr. Lance E Sloan
Mr. Lance E Sloan

Reputation: 3386

Why does TypeScript complain about string index on an enum?

The TypeScript code below has errors about using a string index with an enum in some cases, but not in others. Also, in some environments, the code with "errors" runs anyway.

enum LMSRole {
  AccountAdmin = 'Account Admin',
  SupportConsultant = 'Support Consultant',
  StudentEnrollment = 'StudentEnrollment',
  TeacherEnrollment = 'TeacherEnrollment'
}

console.log(LMSRole.SupportConsultant) /* "Support Consultant" - OK */

// these role strings would be read from input
const roles: String[] = ['AccountAdmin', 'SupportConsultant']
console.log(roles.map(r => LMSRole[r]))
/* Error given, yet runs on (http://typescriptlang.org/play)...
 * Type 'String' cannot be used as an index type. */

 console.log(LMSRole['SupportConsultant']) /* "Support Consultant" - OK */
/* In this example, a type of 'String' is used as an index,
 * but there's no error. Why? */

See this in action at typescriptlang.org's Playground.

What I don't understand is why in the map function, I get the error about using the string index. However, using a string directly to get a single value from the enum works without error.

I also don't understand why the Playground reports an error, but it runs anyway. On my own computer, if I try to run it, it will fail before anything is executed.

UPDATE — When I first wrote this question, I didn't explicitly state that I want to use this enum with strings read from input. (Say, from a text file or stream.) I thought the line beginning const roles would express that intention, but I guess it wasn't clear. I've added a comment to the code to clarify. Would that fact prevent the use of suggested code like typing the array Array<keyof typeof LMSRole> or forcing TS to treat the array as const?

Upvotes: 2

Views: 1035

Answers (1)

Alex Wayne
Alex Wayne

Reputation: 187134

The type string may have more specific types. The type could be specific strings.

So when you do this:

const roles: string[] = ['AccountAdmin', 'SupportConsultant']
console.log(roles.map(r => LMSRole[r]))

roles is an array of string types. But the LMSRole only has very specific strings that you can use as an index, so you get the error.

Where here:

console.log(LMSRole['SupportConsultant'])

Typescript knows, because you directly used a string literal, what the specific type of that string is, and knows it's a good one.

If you changed that to:

LMSRole['SomethingElse']
//Element implicitly has an 'any' type because expression of type '"SomethingElse"' can't be used to index type 'typeof LMSRole'.
//  Property 'SomethingElse' does not exist on type 'typeof LMSRole'.(7053)

Then you get an error that makes sense.

You can force typescript to treat your array as constants, which lets it infer the specific strings types like so:

const roles = ['AccountAdmin', 'SupportConsultant'] as const
console.log(roles.map(r => LMSRole[r])) // no error

Upvotes: 3

Related Questions