import { Id } from './Id'; import { List, toList } from './List'; import { isAn } from './IsA'; import { meta } from './Meta'; import { isDefined, isFunction } from './Is'; import { Validatable } from './Validatable'; import { JsonValue } from './Json'; import { Get, ofGet } from './Get'; import { TypeGuard } from './TypeGuard'; export type EnumConstructor = { all(): List; byIds(ids: Id[]): List; byId(id: Id, alt?: Get): E; isEnum: boolean; }; export const isEnumConstructor = (c: unknown): c is EnumConstructor => isFunction(c) && (c as any).isEnum; export abstract class Enum implements Validatable { static isEnum = true; protected constructor( readonly name: string, readonly id: Id = name.toLowerCase(), readonly code: string = id.toString() ) {} get isValid(): boolean { return isDefined(this.id); } static all(): List { return meta(this.allTuple()).values(); } static filter(p: (value: E, index: number, array: E[]) => unknown, params?: unknown): List { return this.all().filter(p, params); } static first(p?: (value: E, index: number, array: E[]) => unknown, params?: unknown): E { return this.all().first(p, params); } static byIds(ids: Id[] = []): List { return toList(ids) .mapDefined(id => this.byId(id)) .distinct(); } static byId(id: Id, alt?: Get): E { return this.allTuple()[id] ?? ofGet(alt); } protected static allTuple(): Record { return meta(this).get(`all-${this.name}`) ?? meta(this).set(`all-${this.name}`, meta(this).values().filter(isEnum).toObject('id')); } equals(other: E | Id): other is E { return this.id === (isEnum(other) ? other.id : other); } isIn(...items: E[] | Id[]): boolean { return items.some(i => this.equals(i)); } toJSON(): JsonValue { return this.id; } toString(): string { return this.id.toString(); } } export const isEnum: TypeGuard = (e?: unknown): e is Enum => isDefined(e) && e instanceof Enum && isAn(e, 'name', 'id', 'code');