import { Id } from './Id'; import { List, toList } from './List'; import { isAn } from './IsA'; 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; private static readonly tuples = new WeakMap>(); 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 toList(Object.values(this.allTuple())); } 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 { const cached = this.tuples.get(this) as Record | undefined; if (cached) return cached; const tuple = toList([...Object.values(this), ...Object.values(Object.getPrototypeOf(this) ?? {})] as E[]) .filter(isEnum) .toObject('id'); this.tuples.set(this, tuple); return tuple; } 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');