Skip to content

Implement Enum.ofKeys #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 52 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ Typesafe string enums in TypeScript.

* [Installation](#installation)
* [Usage](#usage)
- [Creating and using enums](#creating-and-using-enums)
- [Additional functions](#additional-functions)
- [Enum.isType(enum, value)](#enum-istype-enum-value)
- [Enum.keys(enum)](#enum-keys-enum)
- [Enum.values(enum)](#enum-values-enum)
- [Enum.ofKeys(object)](#enum-ofkeys-object)
* [Motivation](#motivation)
- [Why not built-in enums?](#why-not-built-in-enums)
- [Why not string literals?](#why-not-string-literals)
Expand All @@ -25,6 +31,8 @@ This library requires TypeScript 2.2 or later. If you require TypeScript 2.1 com

## Usage

### Creating and using enums

Define an enum as follows:

``` javascript
Expand Down Expand Up @@ -89,8 +97,39 @@ export type Status = Enum<typeof Status>;
console.log(Status.RUNNING); // -> "running"
```

Several helper functions are provided. First are `Enum.keys()` and `Enum.values()`, which resemble
`Object.keys()` and `Object.values()` but provide strict typing in their return type:
### Additional functions

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

`Enum.isType` checks if a value is of a given enum type and can be used as a type guard. For example:

``` javascript
const Color = Enum("BLACK", "WHITE");
type Color = Enum<typeof Color>;

let selectedColor: Color;
const colorString = getUserInputString(); // Unsanitized string.

// TypeScript error: Type 'string' is not assignable to type '"BLACK" | "WHITE"'.
selectedColor = colorString;

if (Enum.isType(Color, colorString)) {
// No error this time because within type guard.
selectedColor = colorString;
} else {
throw new Error(`${colorString} is not a valid color`);
}
```

#### `Enum.keys(enum)`

Resembles `Object.keys()`, but provides strict typing in its return type.

#### `Enum.values(enum)`

Resembles `Object.values()`, but provides strict typing in its return type.

Example of both `Enum.keys()` and `Enum.values()`:

``` javascript
const FileType = Enum({
Expand All @@ -109,24 +148,22 @@ const values = Enum.values(FileType);
// Return value: ["application/pdf", "text/plain", "image/jpeg"] (not necessarily in that order)
```

Also available is `Enum.isType()`, which checks if a value is of a given enum type and can be used
as a type guard.
#### Enum.ofKeys(object)

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

``` javascript
const Color = Enum("BLACK", "WHITE");
type Color = Enum<typeof Color>;
const ErrorColor = Enum({ OK: "green", ERROR: "red" });
type ErrorColor = Enum<typeof ErrorColor>;

let selectedColor: Color;
const colorString = getUserInputString(); // Unsanitized string.
const ErrorLevel = Enum.ofKeys(ErrorCodes);

// TypeScript error: Type 'string' is not assignable to type '"BLACK" | "WHITE"'.
selectedColor = colorString;
const errorLevel = getErrorLevel();

if (Enum.isType(Color, colorString)) {
// No error because within type guard.
selectedColor = colorString;
} else {
throw new Error(`${colorString} is not a valid color`);
if (errorLevel === ErrorLevel.ERROR) {
...
}
```

Expand Down
9 changes: 9 additions & 0 deletions __tests__/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ describe("Enum", () => {
});
});

describe("Enum.ofKeys", () => {
it("returns an object with keys and values equal to keys of input object", () => {
expect(Enum.ofKeys({
BLACK: "black",
WHITE: "white",
})).toEqual({ BLACK: "BLACK", WHITE: "WHITE" });
});
});

describe("Enum.keys", () => {
it("returns the keys of an enum object", () => {
const e = Enum({
Expand Down
14 changes: 12 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ export function Enum(...values: any[]): object {
export type Enum<T extends object> = T[keyof T];

export namespace Enum {
export function ofKeys<
T extends { [_: string]: any}
>(e: T): { [K in keyof T]: K } {
const result: any = {};
for (const key of Object.keys(e)) {
result[key] = key;
}
return result;
}

export function keys<
T extends { [_: string]: any }
>(e: T): Array<keyof T> {
Expand All @@ -28,8 +38,8 @@ export namespace Enum {
T extends { [_: string]: any }
>(e: T): Array<Enum<T>> {
const result: Array<Enum<T>> = [];
for (const key of keys(e)) {
result.push(e[key as string]);
for (const key of Object.keys(e)) {
result.push(e[key]);
}
return result;
}
Expand Down