Skip to content

Instantly share code, notes, and snippets.

@steida
Created November 13, 2024 20:06
Show Gist options
  • Save steida/d2bfc189d3ab720462c69de11fa4aad1 to your computer and use it in GitHub Desktop.
Save steida/d2bfc189d3ab720462c69de11fa4aad1 to your computer and use it in GitHub Desktop.
// 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