Skip to content

Commit 28087bc

Browse files
committed
Improved "How It Works" section in README
1 parent b95fba8 commit 28087bc

File tree

1 file changed

+27
-15
lines changed

1 file changed

+27
-15
lines changed

README.md

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ console.log(state);
8282
// I hope you're not expecting other services to send you objects that look like this.
8383
```
8484

85-
### Why not string literals
85+
### Why not string literals?
8686

8787
Using string literals throughout a program leaves it vulnerable to bugs caused by typos or
8888
incomplete refactors. For example:
@@ -139,7 +139,10 @@ provide the best of both worlds: string enums with the convenience of built-in e
139139

140140
## How It Works
141141

142-
This section is not necessary to use this library, but for those curious about how it is implemented, read on.
142+
This section is not necessary to use this library, but for those curious about how it is
143+
implemented, read on. The explanation uses the concepts of index types and mapped types, as
144+
described in TypeScript's
145+
[Advanced Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html) page.
143146

144147
The entire source of this library is
145148

@@ -153,19 +156,27 @@ export function Enum<V extends string>(...values: V[]): { [K in V]: K } {
153156
export type Enum<T> = T[keyof T];
154157
```
155158

156-
We are creating a function named `Enum` and a type named `Enum`, so both can be imported with a single symbol.
159+
We are creating a function named `Enum` and a type named `Enum`, so both can be imported with a
160+
single symbol.
157161

158-
The type signature
162+
In TypeScript, a string constant is a type (for example, in `const foo = "Hello"`, the
163+
variable `foo` is assigned type `"Hello"`). This means that the array
159164

160165
``` javascript
161-
function Enum<V extends string>(...values: V[]): { [K in V]: K } {
166+
["RUNNING", "STOPPED"]
162167
```
163168

164-
can be read as follows: take in an array of strings and call the different types `V`. A string
165-
constant is a type in TypeScript (for example, `const foo = "hello"` assigns the type `"hello"`
166-
to `foo`), so `V` is actually multiple string literal types. Then `{ [K in V]: K }` describes an
167-
object whose keys are the types that make up `V` and for each such key has a value equal to that
168-
key. Hence, the type of `Enum("RUNNING", "STOPPED")` is
169+
can be inferred to have type `("RUNNING" | "STOPPED")[]`, and so when it is passed into a function
170+
with type signature
171+
172+
``` javascript
173+
function Enum<V extends string>(...values: V[]): { [K in V]: K }
174+
```
175+
176+
the type parameter `V` is thus inferred to be `"RUNNING" | "STOPPED"`. Then the return type
177+
`{ [K in V]: K }` is a mapped type which describes an object whose keys are the types that
178+
make up `V` and for each such key has a value equal to that key. Hence, the type of
179+
`Enum("RUNNING", "STOPPED")` is
169180

170181
``` javascript
171182
// This is a type, not an object literal.
@@ -181,11 +192,12 @@ Next, consider the definition
181192
type Enum<T> = T[keyof T];
182193
```
183194

184-
This describes, for a given keyed type `T`, the type obtained by taking the values of `T` when
185-
passing in each key (the syntax `T[keyof T]` is meant to evoke `t[key]` for each `key` in `t`). When
186-
applied to the type from the previous step, we end up with the union of the types of the values,
187-
hence `Enum<typeof Enum("RUNNING", "STOPPED")>` evaluates to `"RUNNING" | "STOPPED"`, which is what
188-
we want.
195+
This is an index type which describes, for a given keyed type `T`, the type obtained by indexing
196+
into `T` with an arbitrary one of its keys (the syntax `T[keyof T]` is meant to evoke the
197+
expression `t[key]` for some `key` in `t`). When passing in an arbitrary key to the object from the
198+
previous step, we get a value which might be any one of the object's values, and so its type is thus
199+
the union of the types of the object's values. Hence, `Enum<typeof Enum("RUNNING", "STOPPED")>`
200+
evaluates to `"RUNNING" | "STOPPED"`, which is what we want.
189201

190202
## Acknowledgements
191203

0 commit comments

Comments
 (0)