Derive union of string literal types with lookup types in Typescript
Tags: typescript, javascript
Published: 2021-09-04 07:56:45

---
title: Derive union of string literal types with lookup types in Typescript
published: true
description: Derive union of string literal types with lookup types in Typescript from arrays and objects
tags: typescript,javascript
---

Recently I started playing with unions of string literals as an alternative to enums. I did not experience any issues while using enums. I just prefer the string literals as these, in contrast to enums, do not lead additional code generated during compilation.

One thing that has bothered me so far is that I could not find an easy way to store all strings of a string literal union in an array. This can be useful if you want to e.g. randomly select one of the string literals in the union as part of creating mock/fixture data.  
Of course you can define both the unions of string literals and an array with the same strings:

```typescript
type PostType = 'article' | 'podcast';
const postTypes = ['article', 'podcast'];
```

This duplication is error prone in case a new option needs to be added, removed and the like. So I was searching for a means to either derive the array from the type or the type from the array.  
With lookup types this is exactly possible:

```typescript
const postTypes = ['article', 'podcast'] as const;
type PostTypes = typeof postTypes[number];
```

![Screenshot of derived post types in VSCode](https://github.com/remshams/ts-literal-and-lookup-types/blob/images/images/derived-array-types.png?raw=true)

First `typeof` in combination with `as const` is used to infer the type of the defined array. `as const` is important here as Typescript otherwise defines the type as `Array<string>` instead of an array of string literals.  
Using [indexed access types/lookup types](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html#keyof-and-lookup-types) gives the union of string literals. This is somehow equivalent to using an index in an "normal" Javascript/Typescript to get a specific element like `list[0]`.

Lookup types can be used for more sophisticated use case e.g. like deriving all properties in an object the values of which are e.g. a `string`:

```typescript
type Author = {
  firstName: string;
  lastName: string;
};
type Post = {
  title: string;
  description: string;
  views: number;
  author: Author;
};

type PostStringKeys = {
  [P in keyof Post]: Post[P] extends string ? P : never;
}[keyof Post];
```

![Screenshot of extracted string property names in VSCode](https://github.com/remshams/ts-literal-and-lookup-types/blob/images/images/extracted-string-keys.png?raw=true)

Let's quickly break this down:

- `P keyof Post` gives all keys of `Post` (`title` and `description`).
- `Post[P] extends string ? P : never` checks if the value of property `P` in `Post` is of type `string`. If true the property name is set as value otherwise the property is not included in the newly created type.
- With the help of lookup types the union of property names/string literals is derived using `keyof Post`.
  - The set of keys in `Post` is a superset of the keys of the derived type and can therefor be used as an index

This can be made generic like so:

```typescript
type KeysOfType<T, K> = { [P in keyof T]: T[P] extends K ? P : never }[keyof T];

type PostStringKeys = KeysOfType<Post, string>;
type PostNumberKeys = KeysOfType<Post, number>;
```

Compare to the previous example `T == Post` and `K == string`. This provides the additional possibility to include properties with different value types like `string` and `Author` using unions.

```typescript
type PostStringAndAuthorKeys = KeysOfType<Post, number | Author>;
```

![Screenshot of extracted property names with type number and author](https://github.com/remshams/ts-literal-and-lookup-types/blob/images/images/multiple-extracted-keys.png?raw=true)

The code snippets can be found [here](https://github.com/remshams/ts-literal-and-lookup-types).

That's it and as always, thanks for reading.