66

How can I iterate a string literal type in typescript?

For example i define this type

type Name = "Bill Gates" | "Steve Jobs" | "Linus Torvalds";

I want to iterate like this

for (let name of Name) {
    console.log("Possible name: " + name);
}

Or is this simply not possible in typescript?

Franziskus Karsunke
  • 4,948
  • 3
  • 40
  • 54

6 Answers6

83

Rather than iterating a (union of) string literal types as the OP requested, you can instead define an array literal and if marked as const then the entries' type will be a union of string literal types.

Since typescript 3.4 you can define const assertions on literal expressions to mark that: - no literal types in that expression should be widened (e.g. no going from "hello" to string) - array literals become readonly tuples

For example:

const names = ["Bill Gates", "Steve Jobs", "Linus Torvalds"] as const;
type Names = typeof names[number];

It can be used at runtime and with types checked, for example:

const companies = {
    "Bill Gates" : "Microsoft",
    "Steve Jobs" : "Apple",
    "Linus Torvalds" : "Linux",
} as const;

for(const n of names) {
    console.log(n, companies[n]);
}
const bg : Names = 'Bill Gates';
const ms = companies[bg];

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html#const-assertions

https://mariusschulz.com/blog/const-assertions-in-literal-expressions-in-typescript

https://microsoft.github.io/TypeScript-New-Handbook/chapters/types-from-extraction/#indexed-access-types

Paul Thompson
  • 1,071
  • 1
  • 8
  • 6
  • Excellent, thank you! Do you have a link to the docs where they discuss the `typeof names[number]` syntax? That's new to me. – Mark Edington Jan 09 '20 at 17:20
  • 3
    If you slice the array it becomes an array of the union type: `const n = [...names];` from which you can take `typeof` an entry: `type Names = typeof n[0]` So the syntax `typeof names[number]` appears to be a shorthand syntax for the above. I found out about the syntax `typeof names[number]` yesterday on https://dev.to/andreasbergqvist/typescript-get-types-from-data-using-typeof-4b9c – Paul Thompson Jan 09 '20 at 23:07
  • 1
    The `typeof names[number]` could be extended to `typeof names[keyof typeof names]` -- this works with any object type, not just arrays -- essentially it means "values of `names`". – Jim O'Brien Jul 23 '21 at 06:30
19

Since TypeScript is just a compiler, none of the typing information is present at runtime. This means that unfortunately you cannot iterate through a type.

Depending on what you're trying to do it could be possible for you to use enums to store indices of names that you can then retrieve in an array.

Franziskus Karsunke
  • 4,948
  • 3
  • 40
  • 54
Aron
  • 8,696
  • 6
  • 33
  • 59
4

Since typescript 2.4 it is possible to use string typed enums. These enums can easily be iterated:

https://blogs.msdn.microsoft.com/typescript/2017/06/27/announcing-typescript-2-4/

Franziskus Karsunke
  • 4,948
  • 3
  • 40
  • 54
4

You can't by using Union types. But you can do it with enums:

enum Name {
  'Bill Gates' = 'Bill Gates',
  'Steve Jobs' = 'Steve Jobs',
  'Linus Torvalds' = 'Linus Torvalds'
}

for (const name of Object.keys(Name)) {
  console.log('Possible name: ' + name)
}
Maurici Abad
  • 1,012
  • 9
  • 20
3

AFAIK there is no way to "lift" a string union (type) to runtime JS (value).

The closest solution I found was to use enum: example / related issue.

Community
  • 1
  • 1
Jokester
  • 5,501
  • 3
  • 31
  • 39
1

To add to @Paul Thompson's answer, a pattern I'm trying (to consolidate a large model that we want safety for at compile and runtime) is:

// GiftBasket.ts
// Before:
export type Fruit = 'Apple' | 'Banana';

// After:
export class GiftBasketForm {
  // ...
  edibleGift: Fruit,
  bonusGift: Toy | Fruit,
  // ...
  static AllowedValues: Record<string, readonly string[]> = {
    Fruit: [ 'Apple', 'Banana' ] as const,
    // ... a lot of models/allowed values to organize
  }
}

export type Fruit = typeof GiftBasketForm.AllowedValues.Fruit[number]

// Now you can both:
const fruitSafeAtCompileTime: Fruit = 'Apple';
const myFormAllowedValuesAtRuntime: Fruit[] = GiftBasketForm.AllowedValues.Fruit;

// Or an example case where you might use both at once:
const makeRandomFruit: Fruit = getRandom(GiftBasketForm.AllowedValues.Fruit);
n8m8
  • 41
  • 6