How to define string literal union type from constants in Typescript

TypescriptString Literalstypescript3.0Union Types

Typescript Problem Overview


I know I can define string union types to restrict variables to one of the possible string values:

type MyType = 'first' | 'second'
let myVar:MyType = 'first'

I need to construct a type like that from constant strings, e.g:

const MY_CONSTANT = 'MY_CONSTANT'
const SOMETHING_ELSE = 'SOMETHING_ELSE'
type MyType = MY_CONSTANT | SOMETHING_ELSE

But for some reason it doesn't work; it says MY_CONSTANT refers to a value, but it being used as a type here.

Why does Typescript allow the first example, but doesn't allow the second case? I'm on Typescript 3.4.5

Typescript Solutions


Solution 1 - Typescript

To get the type of a variable you need to use the typeof type operator:

const MY_CONSTANT = 'MY_CONSTANT' // must be const, no annotation. let or var will not work
const SOMETHING_ELSE = 'SOMETHING_ELSE' // must be const, no annotation. let or var will not work
type MyType = typeof MY_CONSTANT | typeof SOMETHING_ELSE

Playground

Note:

Since there seems to be a lot of confusion when people use this. The const matters. If you use other types of declarations (let or var) the final type would be string. Only const preserves string literal types.

Note 2:

For this solution to work you must not specify any type annotation on the const, and let the compiler infer the type of the constants (ex this will not work :const MY_CONSTANT: string = 'MY_CONSTANT')

Solution 2 - Typescript

You can also use enum for this case. For example:

// Define enum.
enum myConstants {
  MY_CONSTANT = 'my_constant',
  SMTH_ELSE = 'smth_else'
}

// Use it in an interface for typechecking.
interface MyInterface {
  myProp: myConstants
}

// Example of correct object - no errors.
let a: MyInterface = {
  myProp: myConstants.MY_CONSTANT
}

// Incorrect value - TS reports an error.
let b: MyInterface = {
  myProp: 'John Doe'
}

More about enums

Solution 3 - Typescript

Enums cover the case quiet well:

export enum ITEM_TYPES {
    TYPE1 = 'text',
    TYPE2 = 'image'
}

export type IItemType = ITEM_TYPES.TYPE1 | ITEM_TYPES.TYPE2

And then in code ITEM_TYPES can be refered to for all kind of runtime comparisons:

if (type === ITEM_TYPES.TYPE1){
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCan PoyrazoğluView Question on Stackoverflow
Solution 1 - TypescriptTitian Cernicova-DragomirView Answer on Stackoverflow
Solution 2 - TypescriptEugeneView Answer on Stackoverflow
Solution 3 - TypescriptMantasView Answer on Stackoverflow