ChatGPT解决这个技术问题 Extra ChatGPT

TypeScript TS7015 error when accessing an enum using a string type parameter

I am new to TypeScript and I don't understand what I need to do to fix the line that generates the TS7015 error (referencing an enum member using a string variable) because the line immediately following that does not error (referencing an enum member using a string literal):

enum State {
    Happy = 0,
    Sad = 1,
    Drunk = 2
}

function Emote(enumKey:string) {
    console.log(State[enumKey]); // error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
    console.log(State["Happy"]); // no error
}

"noImplicitAny": true is set in the project's tsconfig.json the error is detected

"noImplictAny": false is set in the project's tsconfig.json no error is detected

I'm compiling with "ntypescript": "^1.201603060104.1"

I'm now compiling with "tsc": "1.8.10"

C:>npm install -g typescript

`-- typescript@1.8.10

Verifying installation:

C:\>tsc --version

Version 1.8.10

Here's my tsconfig.json file:

{
  "compileOnSave": true,
  "compilerOptions": {
    "target": "ES5",
    "module": "System",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": true,
    "noImplicitAny": true,
    "sourceMap": true,
    "mapRoot": "map/",
    "diagnostics": true
  },
  "exclude": [
    "node_modules",
    "typings"
  ]
}

Here's the compiler output:

C:\>tsc

test.ts(8,17): error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
What kind of answer are you looking for? You've already noted that setting noImplicitAny to false makes the error go away...
Instead of turning off the noImplictAny check, I want to fix the code so that it passes the noImplicitAny check. But more importantly I don't understand why the first reference errors, the second reference passes (typewise the type of both enum references is the same... or perhaps they really aren't and I'm just missing something (probably obvious))
What version of typescript are you using? I am not getting the implicit any type error with that code.
"noImplicitAny": true, Version 1.8.2, no error, State[enumKey]
ntypescript looks like it's mostly for people who want to work directly with the TypeScript compiler API. I think you want typescript...

S
Steven Barnett

If you're using TypeScript 2.1+, you can change enumKey's type to keyof typeof State, like this:

function Emote(enumKey: keyof typeof State) {...}

or, if the function's input is required to be a string, this:

var state : State = State[enumKey as keyof typeof State];

Explanation:

Because enumKey is an arbitrary string, TypeScript doesn't know whether enumKey is the name of a member of State, so it generates an error. TypeScript 2.1 introduced the keyof operator which returns a union of the known, public property names of a type. Using keyof allows us to assert that the property is indeed in the target object.

However, when you create an enum, TypeScript actually produces both a type (which is typically a subtype of number) and a value (the enum object that you can reference in expressions). When you write keyof State, you're actually going to get a union of the literal property names of number. To instead get the property names of the enum object, you can use keyof typeof State.

Sources:

https://github.com/Microsoft/TypeScript/issues/13775#issuecomment-276381229 https://www.typescriptlang.org/docs/handbook/enums.html#enums-at-compile-time


I prefer this one over other solutions where we have to give the benefit of TS. Using this public rateType: keyof typeof RegularTypeEnum | BlockedTypeEnum; anyone can understand the possible values of that property. Thanks.
Your answer is so good, it should be in the official documentation.
I appreciate the answer, but it's irritating how type safety always finds a way to turn very simple and obvious code into an obfuscation of type declarations.
Wow, this answer worked for me, but looking at this makes my head hurt: State[enumKey as keyof typeof State]
K
Ken Smith

I suspect it has to do with TS 1.8.x's new support for string literals in these situations. TS happens to know that "Happy" is a valid string index, but it doesn't know whether enumKey will be or not. You can fix it by casting it to an <any>, like so:

function Emote(enumKey:string) {
    console.log(State[enumKey]); // error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
    console.log(State["Melancholy"]); // error TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
    console.log(State["Happy"]); // no error
    console.log(State[<any>enumKey]); // no error
    console.log(State[<any>"Melancholy"]); // no error
}

(BTW, I think this is new: I couldn't reproduce this error with 1.8.9, but as soon as I upgraded to 1.8.10, I could.)

Also interestingly, I would have expected this to work without the error, but it doesn't:

function TypedEmote(enumKey:'Happy'|'Sad'|'Drunk'){
    console.log(State[enumKey]);
}

Must be something about the TS spec I don't understand, or perhaps they just haven't gotten around to fixing that bit yet.


Interesting that "Melancholy" also exhibits the error -- makes me suspect that there's an (unnamed) derived type of string that the compiler infers and is limited to just "Happy", "Sad", and "Drunk" --- maybe I could explicitly define such a type and use it...
@Neoheurist - That's more-or-less what I was trying to do with the 'Happy'|'Sad'|'Drunk' type in my TypedEmote() method above. And like I said, yeah, I would have thought it should work, but it doesn't seem to. Curious to know if you can find a way to do it.
state: State = State[<any>"Happy"] also doesn't work - "type 'string' is not assignable to type 'State'"
@AlexOkrushko Unfortunately in that situation it seems to need an any type on both sides as I did here if(<any>this.gravity === GravityType[<any>dir]) alternatively, look at Sharpiro's answer, it's a bit more elegant.
H
HolgerJeromin

You can prevent this error with the compiler option without loosing the whole strict null checks

"suppressImplicitAnyIndexErrors": true

https://www.typescriptlang.org/tsconfig#suppressImplicitAnyIndexErrors


s
senshin
var stateName = "Happy"
var state = <State>parseInt(State[<any>stateName]);

This is what I had to do to make the compiler happy


It works, but it's ugly... Steven's solution is much nicer. (But note it's TS 2.1+ only, so YMMV)