0

TypeScript array to string literal type asks and receives an answer regarding how to create a string literal upon array declaration. I am wondering if it is possible to create a string literal from an already existing array.

To take the same basic example:

const furniture = ['chair', 'table', 'lamp'];
type Furniture = 'chair' | 'table' | 'lamp';

the suggested known solution at declaration is:

const furniture = ['chair', 'table', 'lamp'] as const;

This locks the array to a readonly. Is it possible to just take the array and make a new item from it?

const furniture = ['chair', 'table', 'lamp'];
const furntureLiteral = furniture as const;

// Yields: "readonly [string[]]" typing.
// Wanted: "readonly ["chair", "table", "lamp"]" typing.

Or is it impossible due to static typing?

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
aviya.developer
  • 3,343
  • 2
  • 15
  • 41
  • Yes, you are right - due to static typing array needs to be declared as const immideately, since it's impossible to know if it changed between the lines. With const array then you just use `(typeof furniture)[number]`. – Matija Sirk Mar 15 '23 at 11:40
  • you can also define each member as const [Playground Link](https://www.typescriptlang.org/play?#code/MYewdgzgLgBAZgVwE5gJZWQUxgXhgbQHJgALAQ1SUJjIhlEigBoZCoyAjAG02tvvDQWhLmQC2ABz50G0ALoBuALAAoKAE8J2AGLI0GJNjwatIOPD3osqmLZgB6ezAB6AflVA) – Teneff Mar 15 '23 at 11:45
  • 1
    As you've shown if you don't add further metadata when the array's created it's `string[]`. You can't get back to more detail from that. – jonrsharpe Mar 15 '23 at 11:51
  • Is there any value in doing a workaround, of reversing the declarations order: `const furnitureLiteral = ['chair', 'table', 'lamp'] as const; const furniture = [...furnitureLiteral];` does `furniture` have any additional capacities? – aviya.developer Mar 15 '23 at 13:22
  • You could do that but presumably you want `furniture` to be of type `string[]`, so you'd write something like [this](https://tsplay.dev/mbEYdN) maybe? Do you want to see that written up as an answer? – jcalz Mar 15 '23 at 14:30
  • @jcalz this is great, nice addition with the specific `string[]` typing. Now the array has full funcionality. If you write it up as an answer I will accept it and close the thread. – aviya.developer Mar 21 '23 at 11:20

1 Answers1

1

You want to use furniture for two different purposes: remembering the string literal types of the values it's initialized with, but also holding arbitrary other strings in the future. TypeScript doesn't really give you the flexibility to do both with a single variable. The easiest way forward could be to have two variables, one for each purpose.

You can keep track of the initalizer via a const assertion which "locks" the resulting variable:

const initialFurniture = ['chair', 'table', 'lamp'] as const;
type InitialFurniture = typeof initialFurniture[number];
// type InitialFurniture = "chair" | "table" | "lamp"

And then copy that into a mutable string array that lets you do anything you want:

const furniture: string[] = [...initialFurniture];
furniture.push("futon");
furniture.push("hammock");
furniture.push("cupboard");

Now you've separated the concerns and the TypeScript compiler is satisfied also.

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360