Created
November 13, 2024 20:06
-
-
Save steida/d2bfc189d3ab720462c69de11fa4aad1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Promise.withResolvers polyfill for Node.js 20 | |
/* eslint-disable */ | |
Promise.withResolvers || | |
// @ts-expect-error | |
(Promise.withResolvers = function withResolvers() { | |
var a, | |
b, | |
c = new this(function (resolve, reject) { | |
a = resolve; | |
b = reject; | |
}); | |
return { resolve: a, reject: b, promise: c }; | |
}); | |
/* eslint-enable */ | |
// Result | |
export type Result<T, E> = Ok<T> | Err<E>; | |
export interface Ok<T> { | |
ok: true; | |
value: T; | |
} | |
export interface Err<E> { | |
ok: false; | |
error: E; | |
} | |
export const ok = <T>(value: T): Ok<T> => ({ ok: true, value }); | |
export const err = <E>(error: E): Err<E> => ({ ok: false, error }); | |
// Order | |
export interface Order<in A> { | |
(self: A, that: A): -1 | 0 | 1; | |
} | |
// Array | |
export type NonEmptyReadonlyArray<T> = readonly [T, ...(readonly T[])]; | |
export const appendToArray = <T>( | |
item: T, | |
array: ReadonlyArray<T>, | |
): NonEmptyReadonlyArray<T> => | |
[...array, item] as unknown as NonEmptyReadonlyArray<T>; | |
export const mapNonEmptyArray = <T, U>( | |
array: NonEmptyReadonlyArray<T>, | |
mapper: (item: T, index: number) => U, | |
): NonEmptyReadonlyArray<U> => | |
array.map(mapper) as unknown as NonEmptyReadonlyArray<U>; | |
// Record | |
export type ReadonlyRecord<K extends keyof any, V> = Readonly<Record<K, V>>; | |
/** Object.entries does not preserve key branded types, this function does. */ | |
export const recordToEntries = <T extends Record<string, any>>( | |
record: T, | |
): [keyof T, T[keyof T]][] => Object.entries(record) as [keyof T, T[keyof T]][]; | |
/** Object.entries does not preserve key branded types, this function does. */ | |
export const mapRecord = <K extends string, V, U>( | |
record: ReadonlyRecord<K, V>, | |
fn: (value: V, key: K) => U, | |
): ReadonlyRecord<K, U> => | |
Object.fromEntries( | |
Object.entries(record).map(([key, value]) => [ | |
key, | |
fn(value as V, key as K), | |
]), | |
) as ReadonlyRecord<K, U>; | |
export const filterRecord = <T extends ReadonlyRecord<string, any>>( | |
obj: T, | |
predicate: (value: T[keyof T], key: keyof T) => boolean, | |
/** | |
* Partial<T> is a lie because there is no reason why filterRecord should | |
* change value types but Partial<T> adds `| undefined`. I'm not sure whether | |
* it's even possible to type properly. | |
*/ | |
): Partial<T> => { | |
const result: Partial<T> = {}; | |
const keys = Object.keys(obj) as Array<keyof T>; | |
for (const key of keys) { | |
const value = obj[key]; | |
if (predicate(value, key)) { | |
result[key] = value; | |
} | |
} | |
return result; | |
}; | |
// Predicates | |
export type Predicate<T> = (value: unknown) => value is T; | |
export const isNumber: Predicate<number> = (input) => typeof input === "number"; | |
export const isString: Predicate<string> = (value) => typeof value === "string"; | |
export const isUint8Array: Predicate<Uint8Array> = (value) => | |
value instanceof Uint8Array; | |
export const isNonEmptyArray = <T>( | |
array: readonly T[], | |
): array is NonEmptyReadonlyArray<T> => array.length > 0; | |
// Consts | |
export const constVoid = (): void => { | |
// | |
}; | |
// TypedWorker | |
/** Platform-independent worker abstraction for background processing. */ | |
export interface TypedWorker<Input, Output> { | |
/** Sends a message of type `InputType` to the worker. */ | |
postMessage: (message: Input) => void; | |
/** Sets a callback for messages of type `OutputType` from the worker. */ | |
onMessage: (callback: (message: Output) => void) => void; | |
} | |
export type TypedWorkerAsyncHandlers<T extends { type: string }> = { | |
[K in T["type"]]: (input: Extract<T, { type: K }>) => Promise<void>; | |
}; | |
export type TypedWorkerSyncHandlers<T extends { type: string }> = { | |
[K in T["type"]]: (input: Extract<T, { type: K }>) => void; | |
}; | |
export type TypedWorkerOnMessage<T> = (message: T) => void; | |
export type CallbackWithoutParams = () => void; | |
/** Helper function to ensure exhaustive matching in a switch statement. */ | |
export const exhaustiveCheck = (value: never): never => { | |
throw new Error(`Unhandled case: ${JSON.stringify(value)}`); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment