You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
52 lines
1.1 KiB
TypeScript
52 lines
1.1 KiB
TypeScript
export type Ref<T> = { __ref: keyof T };
|
|
export const ref = <T>(k: keyof T) => ({ __ref: k });
|
|
const isRef = <T>(t: unknown): t is Ref<T> =>
|
|
t !== null && typeof t === "object" && "__ref" in t;
|
|
|
|
export type Ouro<T> = {
|
|
[K in keyof T]: T[K] | Ref<T>
|
|
};
|
|
|
|
type Follow<T, K extends keyof T> =
|
|
T[K] extends Ref<T>
|
|
? Follow<T, T[K]["__ref"]>
|
|
: T[K];
|
|
|
|
export type Boros<T> = {
|
|
[K in keyof T]: Follow<T, K>
|
|
};
|
|
|
|
export const resolve = <T>(obj: Ouro<T>): Boros<T> => {
|
|
const newObj = {} as Partial<Boros<T>>;
|
|
const keysToProcess = new Set(Object.keys(obj) as (keyof typeof obj)[]);
|
|
while (true) {
|
|
if (keysToProcess.size === 0) {
|
|
break;
|
|
}
|
|
|
|
const keysToAssign = new Set<keyof T>();
|
|
|
|
let k = [...keysToProcess][0];
|
|
while (true) {
|
|
if (keysToAssign.has(k)) {
|
|
throw new Error("Infinite loop detected");
|
|
}
|
|
|
|
keysToProcess.delete(k);
|
|
keysToAssign.add(k);
|
|
|
|
const v = newObj[k] ?? obj[k];
|
|
if (!isRef<T>(v)) {
|
|
for (const k of keysToAssign) {
|
|
newObj[k] = v as Boros<T>[typeof k];
|
|
}
|
|
break;
|
|
}
|
|
|
|
k = v.__ref;
|
|
}
|
|
}
|
|
|
|
return newObj as Boros<T>;
|
|
};
|