9

I am trying to pass a string enum into a function that expects a string. The catch is that this string enum has to be assigned from a (global) constants enum that holds all the constants in our repository.

enum Constants {
    hello = "Hello"
}

enum Potato {
    h = Constants.hello
}

function takesAString(s: string) {
    console.log(s + ", world!");
}
takesAString(Potato.h);
// ERROR: Argument of type 'Potato' is not assignable to parameter of type 'string'

While one would expect that Potato.h is of type string (since it is being assigned a string from the string enum Constants), in fact it errors out, with the error that 'Potato' is not assignable to parameter of type string. This implies to me that the Typescript compiler cannot infer that Potato.h is a string.

Things that work:

enum Potato {
    h = "Hello"
}

function takesAString(s: string) {
    console.log(s + ", world!");
}
takesAString(Potato.h);
// OK
enum Constants {
    hello = "Hello"
}

enum Potato {
    h = Constants.hello
}

function takesAString(s: string) {
    console.log(s + ", world!");
}
takesAString(Potato.h.toString());
// OK: toString() causes "Hello, world!" to be printed

I'm working with Typescript version 3.8.3

Playground Link

hernytan
  • 133
  • 1
  • 2
  • 5
  • 1
    Do you really need enums in this scenario? I think it'd be much easier to just use objects. I'd just make Constants and Potato const objects and I think TypeScript would be happy – thelukaszns Sep 30 '20 at 21:29
  • @thelukaszns while that is something I would like, it's a little too late since this is how the entire codebase (>100 files) is structured. – hernytan Sep 30 '20 at 22:47

1 Answers1

6

This looks like a bug in typescript, I raised a bug report here It looks like typescript is typing the Potato enum as being numbers which is obviously wrong.

string enums aren't allowed to have computed members, for instance if you do this:

declare function returnsString(): string;
enum Test {
    a = returnsString();
} 

you get this error:

Only numeric enums can have computed members, but this expression has type 'string'. If you do not need exhaustiveness checks, consider using an object literal instead.

So you probably want to use object literals, it won't require rewriting the whole codebase just changing the enums to something like this:

type Constants = typeof Constants[keyof typeof Constants]
const Constants = {
    hello: "Hello"
} as const

type Potato = typeof Potato[keyof typeof Potato]
const Potato = {
    h: Constants.hello
} as const;
Tadhg McDonald-Jensen
  • 20,699
  • 5
  • 35
  • 59
  • 1
    Just mentioning [microsoft/TypeScript#40862](https://github.com/microsoft/TypeScript/issues/40862) here by number so that searches for #40862 might find this. – jcalz Jan 27 '22 at 21:11