Profectus/src/util/computed.ts
2022-03-08 19:40:51 -06:00

50 lines
1.8 KiB
TypeScript

import { computed, Ref } from "vue";
import { isFunction } from "./common";
export const DoNotCache = Symbol("DoNotCache");
export type Computable<T> = T | Ref<T> | (() => T);
export type ProcessedComputable<T> = T | Ref<T>;
export type GetComputableType<T> = T extends { [DoNotCache]: true }
? T
: T extends () => infer S
? Ref<S>
: undefined extends T
? undefined
: T;
export type GetComputableTypeWithDefault<T, S> = undefined extends T
? S
: GetComputableType<NonNullable<T>>;
export type UnwrapComputableType<T> = T extends Ref<infer S> ? S : T extends () => infer S ? S : T;
export type ComputableKeysOf<T> = Pick<
T,
{
[K in keyof T]: T[K] extends Computable<unknown> ? K : never;
}[keyof T]
>;
// TODO fix the typing of this function, such that casting isn't necessary and can be used to
// detect if a createX function is validly written
export function processComputable<T, S extends keyof ComputableKeysOf<T>>(
obj: T,
key: S
): asserts obj is T & { [K in S]: ProcessedComputable<UnwrapComputableType<T[S]>> } {
const computable = obj[key];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (isFunction(computable) && computable.length === 0 && !(computable as any)[DoNotCache]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
obj[key] = computed(computable.bind(obj));
}
}
export function convertComputable<T>(obj: Computable<T>): ProcessedComputable<T> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (isFunction(obj) && !(obj as any)[DoNotCache]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
obj = computed(obj);
}
return obj as ProcessedComputable<T>;
}