Skip to content

Commit d3f308b

Browse files
committed
Add obsolescence notice to README
1 parent 7a16aff commit d3f308b

File tree

2 files changed

+109
-60
lines changed

2 files changed

+109
-60
lines changed

README.md

Lines changed: 108 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
# TypeScript String Enums
22

3-
Typesafe string enums in TypeScript.
3+
Typesafe string enums in TypeScript pre-2.4.
44

5-
[![Build Status](https://travis-ci.org/dphilipson/typescript-string-enums.svg?branch=master)](https://travis-ci.org/dphilipson/typescript-string-enums)
5+
[![Build
6+
Status](https://travis-ci.org/dphilipson/typescript-string-enums.svg?branch=master)](https://travis-ci.org/dphilipson/typescript-string-enums)
7+
8+
**As of TypeScript 2.4, this library is made mostly obsolete by native string
9+
enums** (see the
10+
[announcement](https://blogs.msdn.microsoft.com/typescript/2017/06/27/announcing-typescript-2-4/)).
11+
I recommend that most users who are on at least TypeScript 2.4 now use native
12+
enums instead. There are still a few minor reasons to continue to use this
13+
library, as discussed in the [Advantages over native string
14+
enums](#advantages-over-native-string-enums) section.
615

716
## Table of Contents
817

@@ -18,6 +27,7 @@ Typesafe string enums in TypeScript.
1827
- [Why not built-in enums?](#why-not-built-in-enums)
1928
- [Why not string literals?](#why-not-string-literals)
2029
* [How It Works](#how-it-works)
30+
* [Advantages over native string enums](#advantages-over-native-string-enums)
2131
* [Acknowledgements](#acknowledgements)
2232

2333
## Installation
@@ -26,8 +36,8 @@ Typesafe string enums in TypeScript.
2636
npm install --save typescript-string-enums
2737
```
2838

29-
This library requires TypeScript 2.2 or later. If you require TypeScript 2.1 compatibility, version
30-
0.2.0 of this library is the last one with support.
39+
This library requires TypeScript 2.2 or later. If you require TypeScript 2.1
40+
compatibility, version 0.2.0 of this library is the last one with support.
3141

3242
## Usage
3343

@@ -80,9 +90,10 @@ function saySomethingAboutState(state: State) {
8090
}
8191
```
8292

83-
Instead of a list of values, an object may be passed instead if it is desired that the string values
84-
be different from the constant names. This also has the advantage of allowing JSDoc comments to be
85-
specified on individual values. For example:
93+
Instead of a list of values, an object may be passed instead if it is desired
94+
that the string values be different from the constant names. This also has the
95+
advantage of allowing JSDoc comments to be specified on individual values. For
96+
example:
8697

8798
``` javascript
8899
export const Status = Enum({
@@ -101,7 +112,8 @@ console.log(Status.RUNNING); // -> "running"
101112

102113
#### `Enum.isType(enum, value)`
103114

104-
`Enum.isType` checks if a value is of a given enum type and can be used as a type guard. For example:
115+
`Enum.isType` checks if a value is of a given enum type and can be used as a
116+
type guard. For example:
105117

106118
``` javascript
107119
const Color = Enum("BLACK", "WHITE");
@@ -150,9 +162,10 @@ const values = Enum.values(FileType);
150162

151163
#### Enum.ofKeys(object)
152164

153-
Creates a new enum with the same keys as the provided enum or object and whose values are equal to
154-
its keys. This is most useful if for some reason it is necessary to do string comparisons against
155-
the keys of an enum rather than the values. For example:
165+
Creates a new enum with the same keys as the provided enum or object and whose
166+
values are equal to its keys. This is most useful if for some reason it is
167+
necessary to do string comparisons against the keys of an enum rather than the
168+
values. For example:
156169

157170
``` javascript
158171
const ErrorColor = Enum({ OK: "green", ERROR: "red" });
@@ -169,15 +182,17 @@ if (errorLevel === ErrorLevel.ERROR) {
169182

170183
## Motivation
171184

172-
Enums are useful for cleanly specifying a type that can take one of a few specific values.
173-
TypeScript users typically implement enums in one of two ways: built-in
174-
[TypeScript enums](https://www.typescriptlang.org/docs/handbook/enums.html) or string literals, but
175-
each of these has drawbacks.
185+
Enums are useful for cleanly specifying a type that can take one of a few
186+
specific values. TypeScript users typically implement enums in one of two ways:
187+
built-in [TypeScript
188+
enums](https://www.typescriptlang.org/docs/handbook/enums.html) or string
189+
literals, but each of these has drawbacks.
176190

177191
### Why not built-in enums?
178192

179-
Built-in enums have one big drawback. Their runtime value is a number, which is annoying during
180-
development and makes them unsuitable for use with external APIs.
193+
Built-in enums have one big drawback. Their runtime value is a number, which is
194+
annoying during development and makes them unsuitable for use with external
195+
APIs.
181196

182197
``` javascript
183198
enum Status {
@@ -199,18 +214,20 @@ type Status = "RUNNING" | "STOPPED";
199214
type TriathlonStage = "SWIMMING" | "CYCLING" | "RUNNING";
200215
```
201216

202-
Then if at a later stage I want to change `Status` to be `"STARTED" | "STOPPED"`, there's no easy
203-
way to do it. I can't globally find/replace `"RUNNING"` to `"STARTED"` because it will also change
204-
the unrelated string constants representing `TriathlonStage`. Instead, I have to examine every
205-
occurrance of the string `"RUNNING"` to see if it needs to change. Besides, these kinds of global
206-
non-semantic substitutions should make you nervous.
217+
Then if at a later stage I want to change `Status` to be `"STARTED" |
218+
"STOPPED"`, there's no easy way to do it. I can't globally find/replace
219+
`"RUNNING"` to `"STARTED"` because it will also change the unrelated string
220+
constants representing `TriathlonStage`. Instead, I have to examine every
221+
occurrance of the string `"RUNNING"` to see if it needs to change. Besides,
222+
these kinds of global non-semantic substitutions should make you nervous.
207223

208-
Another disadvantage of string literals comes when using IDE autocomplete features. It's convenient
209-
to be able to type `Status.` and have autocomplete suggest `Status.RUNNING` and `Status.STOPPED`,
210-
but with string literals no such suggestion is possible.
224+
Another disadvantage of string literals comes when using IDE autocomplete
225+
features. It's convenient to be able to type `Status.` and have autocomplete
226+
suggest `Status.RUNNING` and `Status.STOPPED`, but with string literals no such
227+
suggestion is possible.
211228

212-
I might try to solve both problems by introducing constants for the string literals, but this has
213-
issues as well:
229+
I might try to solve both problems by introducing constants for the string
230+
literals, but this has issues as well:
214231

215232
``` javascript
216233
// Typo on "STOPPED" not caught by anything below without additional boilerplate.
@@ -234,15 +251,16 @@ const Status = {
234251
};
235252
```
236253

237-
This library is effectively a programmatic version of these repetitive definitions. It attempts to
238-
provide the best of both worlds: string enums with the convenience of built-in enums.
254+
This library is effectively a programmatic version of these repetitive
255+
definitions. It attempts to provide the best of both worlds: string enums with
256+
the convenience of built-in enums.
239257

240258
## How It Works
241259

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

247265
The relevant type declarations are as follows:
248266

@@ -258,22 +276,24 @@ function Enum<
258276
type Enum<T> = T[keyof T];
259277
```
260278

261-
We are creating a overloaded function named `Enum` and a type named `Enum`, so both can be imported
262-
with a single symbol.
279+
We are creating a overloaded function named `Enum` and a type named `Enum`, so
280+
both can be imported with a single symbol.
263281

264-
Consider the first overload, which handles the case of variadic arguments representing the enum
265-
values. In TypeScript, a string constant is a type (for example, in `const foo = "Hello"`, the
266-
variable `foo` is assigned type `"Hello"`). This means that the array
282+
Consider the first overload, which handles the case of variadic arguments
283+
representing the enum values. In TypeScript, a string constant is a type (for
284+
example, in `const foo = "Hello"`, the variable `foo` is assigned type
285+
`"Hello"`). This means that the array
267286

268287
``` javascript
269288
["RUNNING", "STOPPED"]
270289
```
271290

272-
can be inferred to have type `("RUNNING" | "STOPPED")[]`, and so when it is passed into a function
273-
with the above type signature, the type parameter `V` is thus inferred to be
274-
`"RUNNING" | "STOPPED"`. Then the return type `{ [K in V]: K }` is a mapped type which describes an
275-
object whose keys are the types that make up `V` and for each such key has a value of the same type
276-
as that key. Hence, the type of `Enum("RUNNING", "STOPPED")` is
291+
can be inferred to have type `("RUNNING" | "STOPPED")[]`, and so when it is
292+
passed into a function with the above type signature, the type parameter `V` is
293+
thus inferred to be `"RUNNING" | "STOPPED"`. Then the return type `{ [K in V]: K
294+
}` is a mapped type which describes an object whose keys are the types that make
295+
up `V` and for each such key has a value of the same type as that key. Hence,
296+
the type of `Enum("RUNNING", "STOPPED")` is
277297
278298
``` javascript
279299
// This is a type, not an object literal.
@@ -283,19 +303,20 @@ as that key. Hence, the type of `Enum("RUNNING", "STOPPED")` is
283303
}
284304
```
285305

286-
Next, consider the second overload, which handles the case which takes an object of keys and values,
287-
and for the sake of example consider
306+
Next, consider the second overload, which handles the case which takes an object
307+
of keys and values, and for the sake of example consider
288308

289309
``` javascript
290310
const Status = Enum({
291311
RUNNING: "running",
292312
STOPPED: "stopped",
293313
});
294314
```
295-
The second type parameter `V` is inferred as `"running" | "stopped"`, which forces TypeScript to
296-
infer the first type parameter `T` as an object whose values are the specific string values that
297-
make up `V`. Hence, even though `{ RUNNING: "running", "STOPPED": "stopped" }` would have type
298-
`{ RUNNING: string; STOPPED: string; }`, passing it through `Enum` causes its type to be inferred
315+
The second type parameter `V` is inferred as `"running" | "stopped"`, which
316+
forces TypeScript to infer the first type parameter `T` as an object whose
317+
values are the specific string values that make up `V`. Hence, even though `{
318+
RUNNING: "running", "STOPPED": "stopped" }` would have type `{ RUNNING: string;
319+
STOPPED: string; }`, passing it through `Enum` causes its type to be inferred
299320
instead as the desired
300321

301322
``` javascript
@@ -312,18 +333,46 @@ Next, consider the definition
312333
type Enum<T> = T[keyof T];
313334
```
314335

315-
This is an index type which describes, for a given keyed type `T`, the type obtained by indexing
316-
into `T` with an arbitrary one of its keys (the syntax `T[keyof T]` is meant to evoke the
317-
expression `t[key]` for some `key` in `t`). When passing in an arbitrary key to the object from the
318-
previous step, we get a value which might be any one of the object's values, and so its type is thus
319-
the union of the types of the object's values. Hence, `Enum<typeof Enum("RUNNING", "STOPPED")>`
320-
evaluates to `"RUNNING" | "STOPPED"`, which is what we want.
336+
This is an index type which describes, for a given keyed type `T`, the type
337+
obtained by indexing into `T` with an arbitrary one of its keys (the syntax
338+
`T[keyof T]` is meant to evoke the expression `t[key]` for some `key` in `t`).
339+
When passing in an arbitrary key to the object from the previous step, we get a
340+
value which might be any one of the object's values, and so its type is thus the
341+
union of the types of the object's values. Hence, `Enum<typeof Enum("RUNNING",
342+
"STOPPED")>` evaluates to `"RUNNING" | "STOPPED"`, which is what we want.
343+
344+
## Advantages over native string enums
345+
346+
With the addition of native string enums in TypeScript 2.4, this library will be
347+
unnecessary for most users. There are still a few niche reasons why users may
348+
still prefer to use this library.
349+
350+
* This library provides several helper functions which cannot easy be
351+
implemented for native enums. Of these, `Enum.isType()` will likely be the
352+
most useful.
353+
* Defining a native string enum involves a bit of repetition, as each value must
354+
be written twice:
355+
```ts
356+
enum Color {
357+
RED = "RED",
358+
GREEN = "GREEN",
359+
BLUE = "BLUE"
360+
}
361+
```
362+
vs
363+
```ts
364+
const Color = Enum("RED", "GREEN", "BLUE");
365+
type Color = Enum<typeof Color>;
366+
```
367+
If there are many values, it may be desirable to avoid the repetition and
368+
corresponding possibility for typos.
321369

322370
## Acknowledgements
323371

324-
This libary is heavily inspired by posts in
325-
[this thread](https://github.com/Microsoft/TypeScript/issues/3192). In particular, credit goes to
326-
users **[@igrayson](https://github.com/igrayson)**, **[@nahuel](https://github.com/nahuel)**,
327-
and **[@kourge](https://github.com/kourge)**.
372+
This libary is heavily inspired by posts in [this
373+
thread](https://github.com/Microsoft/TypeScript/issues/3192). In particular,
374+
credit goes to users **[@igrayson](https://github.com/igrayson)**,
375+
**[@nahuel](https://github.com/nahuel)**, and
376+
**[@kourge](https://github.com/kourge)**.
328377

329378
Copyright © 2017 David Philipson

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "typescript-string-enums",
33
"version": "0.3.4",
4-
"description": "Typesafe string enums in TypeScript.",
4+
"description": "Typesafe string enums in TypeScript pre-2.4.",
55
"main": "dist/index.js",
66
"types": "dist/index",
77
"files": [

0 commit comments

Comments
 (0)