Skip to content

Instantly share code, notes, and snippets.

@mceachen
Last active July 4, 2020 22:14
Show Gist options
  • Save mceachen/b6f2537324da36d0919e2fd4b9cbed8e to your computer and use it in GitHub Desktop.
Save mceachen/b6f2537324da36d0919e2fd4b9cbed8e to your computer and use it in GitHub Desktop.
TypeScript enumerations
export const Directions = strEnum("North", "South", "East", "West")
export type Direction = StrEnumKeys<typeof Directions>
// Generally useful types:
export type Maybe<T> = T | undefined
export type MaybeNull<T> = Maybe<T> | null | void
// See https://basarat.gitbooks.io/typescript/content/docs/types/literal-types.html
export type StrEnumType<T extends string> = {
[K in T]: K
}
export type StrEnumHelpers<T extends string> = {
values: T[]
length: number
has(s: MaybeNull<string>): s is T
indexOf(s: MaybeNull<string>): number
validOrElse<R>(s: MaybeNull<string>, defaultValue: () => R): T | R
map<R>(s: MaybeNull<string>, f: (t: T) => R): Maybe<R>
}
export type StrEnum<T extends string> = StrEnumType<T> & StrEnumHelpers<T>
export type StrEnumKeys<Type> = Type extends StrEnum<infer X> ? X : never
export function strEnum<T extends string>(...o: T[]): StrEnum<T> {
const values = Object.freeze(o) as T[]
const dict: StrEnumType<T> = {} as any
for (const ea of values) {
dict[ea] = ea
}
const has = (s: MaybeNull<string>): s is T =>
s != null && values.includes(s as T)
const indexOf = (s: MaybeNull<string>) =>
s == null ? -1 : values.indexOf(s as T)
const validOrElse = <R>(s: MaybeNull<string>, defaultValue: () => R) =>
has(s) ? s : defaultValue()
const map = <R>(s: string, f: (t: T) => R) => (has(s) ? f(s as T) : undefined)
return {
...dict,
values,
length: values.length,
has,
indexOf,
validOrElse,
map
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment