1/**
2 * helpers for the typescript language
3 *
4 * @module ts
5 */
6
7/** return type of `setTimeout` regardless of the environment */
8export type Timer = ReturnType<typeof setTimeout>;
9/** return type of `setInterval` regardless of the environment */
10export type Interval = ReturnType<typeof setInterval>;
11/** opposite of the built-in `Readonly` type */
12export type Writeable<T> = { -readonly [P in keyof T]: T[P] };
13/** opposite of the built-in `Readonly` type, but recursive */
14// TODO: tuples?
15export type DeepWriteable<T> = {
16 -readonly [P in keyof T]: T[P] extends ReadonlyArray<infer I> ? Array<I>
17 : DeepWriteable<T[P]>;
18};
19
20/**
21 * redeclared here because it is only provided by `lib: ["dom"]` and not
22 * the JavaScript standard types.
23 */
24export interface VoidFunction {
25 (): void;
26}
27
28/** Convenience type. Construct with `defer` */
29export interface Dispose extends Disposable, VoidFunction {}
30
31/**
32 * ```ts
33 * using _ = ts.defer(() => { ... });
34 * ```
35 * Additionally, this can be used to construct the convenience type
36 * `ts.Dispose`
37 */
38export function defer(fn: () => void): Dispose {
39 const f = fn as Dispose;
40 f[Symbol.dispose] = f;
41 return f;
42}
43
44export type Mixin<A, B> = A & B;
45
46/**
47 * mutates `base` to implement all methods of `other`, using `other` as state.
48 */
49export function mixin<A extends object, B extends object>(
50 base: A,
51 other: B,
52): Mixin<A, B> {
53 const prototype = Object.getPrototypeOf(other);
54 for (
55 const key of [
56 ...Object.getOwnPropertyNames(prototype),
57 ...Object.getOwnPropertySymbols(prototype),
58 ]
59 ) {
60 if (key === "constructor") continue;
61 if (key in base) continue;
62 const fn = prototype[key];
63 if (typeof fn !== "function") continue;
64 (base as Record<string | symbol, unknown>)[key] = fn.bind(other);
65 }
66 return base as A & B;
67}
68
69export type EmptyObject = Record<never, unknown>;
70
71/** TODO: this type has many subtle bugs */
72export type ToJson<T> = T extends JsonValue ? T
73 : T extends { toJSON(): infer J } ? ToJson<J>
74 : T extends Set<unknown> | Map<unknown, unknown> | Record<string, never> ? EmptyObject
75 : T extends unknown[] ? {
76 [K in keyof Omit<T, keyof unknown[]>]: T[K] extends undefined | void ? null
77 : ToJson<T[K]>;
78 }
79 : T extends object ? { [K in keyof Omit<T, symbol>]: ToJson<T[K]> }
80 : never;
81export type Json = JsonValue | undefined | void;
82export type JsonValue =
83 | string
84 | number
85 | boolean
86 | null
87 | JsonValue[]
88 | { [k: string]: Exclude<JsonValue, undefined> };