2

I have this literal type export type names = 'n1' | 'n2' | 'n3' | 'n4' | 'n5' | 'n6';

I wonder how would you iterate that type ?

May be you can convert that type in something else and iterate that?

Should you redefine the type in a different way?

names.forEach(value => {
  console.log(value);
}); 
Sergino
  • 10,128
  • 30
  • 98
  • 159
  • 2
    What do you mean, "iterate type"? A type does not exist in the compiled Javascript – CertainPerformance Apr 01 '20 at 01:25
  • I don't think you can enumerate the types in a type declaration like that; you could change it to a string enum though, and then you should be able to enumerate the values. – Dave Cousineau Apr 01 '20 at 01:27
  • @DaveCousineau do you have a sample? – Sergino Apr 01 '20 at 01:28
  • Possible duplicate of [TypeScript String Union to String Array](https://stackoverflow.com/questions/44480644/typescript-string-union-to-string-array). You should define the `names` array first and have the compiler infer the `names` type from it, not the other way around (which is not possible) – jcalz Apr 01 '20 at 01:30

2 Answers2

6

Short Answer

You could define it as a const and a type like this:

const names = ['n1' , 'n2' , 'n3' , 'n4' , 'n5' , 'n6'] as const;

// This produces the union type 'n1' | 'n2' | 'n3' | 'n4' | 'n5' | 'n6';
type names = typeof names[number];

// use names as a type here
const n1: names = 'n1';

console.log({
  names,
  n1,
  // use names as an array here
  mapped: names.map(name => `I am ${name}`)
});

Explanation and Demo

What is going on here?

The as const creates an array with a const context. That means the array is not a string array but is a readonly array of specific string literal values.

Then, the typeof names[number] uses an indexed access operator to extract those string literal values into a union type.

If we did not use as const to define our array, then the typeof names[number] would give us string type instead of a union of the array's string literal values.

The end result is pretty neat. We can use names as a union type for type checking and as an array at runtime.

Here it is in the playground, and here is the playground output in JavaScript:

"use strict";
const names = ['n1', 'n2', 'n3', 'n4', 'n5', 'n6'];
const n1 = 'n1';
console.log({
names,
n1,
mapped: names.map(name => `I am ${name}`)
});

Caveat: The use of names as both a union type an an array value raises a question about naming conventions. Usually types are PascalCased (e.g. Names) and values are camelCased (e.g. names). Which naming conventions ought we to follow here?

For completeness, here is how it looks in VS Code across two files:

enter image description here

Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467
  • why do you need this line `type names = typeof names[number];` ? – Sergino Apr 01 '20 at 01:38
  • @sreginogemoh the `typeof names[number]` uses an indexed access operator to get the string literal types of the array values. – Shaun Luttin Apr 01 '20 at 01:44
  • seems like there is something wrong with `names.forEach(name => name)` if you check the playground url it output `Array(6) [ "n1", "n2", "n3", "n4", "n5", "n6" ] n1 undefined` – Sergino Apr 01 '20 at 01:50
  • @sreginogemoh Nice catch. Fixed. – Shaun Luttin Apr 01 '20 at 01:55
  • @sreginogemoh `const names` and `type names` have the same name, because I read your requirements as 1. define a string literal type and 2. enable iterating over that string literal "type". We did that by giving `const names` and `type names` the same name. That way we fulfill (1) by creating a type, and we fulfill (2) by creating a value *with the same name* as the type. – Shaun Luttin Apr 01 '20 at 02:04
  • if I have my literals in separate files I had to export `type names`, right? – Sergino Apr 01 '20 at 02:05
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/210708/discussion-between-shaun-luttin-and-sreginogemoh). – Shaun Luttin Apr 01 '20 at 02:06
  • hey, there are you still in the chat? – Sergino Apr 01 '20 at 02:31
3

Types do not exist in the compiled code - there is nothing emitted to iterate over.

If you need the union type as shown in the question and need to be able to iterate over it as an array, first create the array as const, and define the type as the array's values:

const arr = ['n1', 'n2', 'n3', 'n4', 'n5', 'n6'] as const;
export type names = typeof arr[number];
for (const num of arr) {
  console.log(num);
}
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • why do you need this like `export type names = typeof arr[number];` ? – Sergino Apr 01 '20 at 01:39
  • 1
    `typeof arr[number]` will give you the type of accessing a `number` property on the `arr` - eg `arr[0]`, `arr[1]`, etc, and those accesses result in a value of `'n1'`, or `'n2'`, etc - it creates a union type of all values in the array. – CertainPerformance Apr 01 '20 at 01:42
  • i just removed `export type names = typeof arr[number];` seems like `for` loop still works... – Sergino Apr 01 '20 at 01:51
  • If you don't actually need the type union described in the question, then feel free to remove it – CertainPerformance Apr 01 '20 at 01:52