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'.
noImplicitAny
to false
makes the error go away...
ntypescript
looks like it's mostly for people who want to work directly with the TypeScript compiler API. I think you want typescript
...
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 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.
'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'"
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.
You can prevent this error with the compiler option without loosing the whole strict null checks
"suppressImplicitAnyIndexErrors": true
https://www.typescriptlang.org/tsconfig#suppressImplicitAnyIndexErrors
var stateName = "Happy"
var state = <State>parseInt(State[<any>stateName]);
This is what I had to do to make the compiler happy
Success story sharing
public rateType: keyof typeof RegularTypeEnum | BlockedTypeEnum;
anyone can understand the possible values of that property. Thanks.State[enumKey as keyof typeof State]