84

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?

1

6 Answers 6

101

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 Name = typeof names[number];

Iterating that at runtime:

for(const n of names) {
  const val : Name = n;
  console.log(val);
}

It can often be more useful to define it as an object, especially if there's value information to associate to the entries:

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

type Person = keyof typeof companies;
type Company = typeof companies[Person];

for(const n of names) {
  const p : Person = n;
  const c : Company = companies[p];
  console.log(p, c);
}

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://www.typescriptlang.org/docs/handbook/2/indexed-access-types.html

Sign up to request clarification or add additional context in comments.

3 Comments

Excellent, thank you! Do you have a link to the docs where they discuss the typeof names[number] syntax? That's new to me.
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 dev.to/andreasbergqvist/…
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".
23

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.

Comments

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)
}

Comments

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.

Comments

3

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/

1 Comment

The questions was about "string literal type" not "enum"
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);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.