Skip to content

Instantly share code, notes, and snippets.

@emilien-jegou
Last active February 17, 2024 16:42
Show Gist options
  • Save emilien-jegou/1564bce3e6a312a3d5bfbc3252960b12 to your computer and use it in GitHub Desktop.
Save emilien-jegou/1564bce3e6a312a3d5bfbc3252960b12 to your computer and use it in GitHub Desktop.
Pattern matching on key
export type ObjectPick<
T extends Record<string, unknown>,
K extends keyof T,
V extends T[K],
> = T extends Record<K, infer U> ? ([U] extends [V] ? T : never) : never;
type ObjectOmit<
T extends Record<string, unknown>,
K extends keyof T,
V extends T[K],
> = T extends Record<K, infer U> ? ([U] extends [V] ? never : T) : T;
export type OnlyString<X> = X extends string ? X : never;
type MatchRet<T extends Record<string, unknown>, K extends keyof T, ReturnDataType> = {
when: <V extends T[K], RetWhen>(
value: V | V[],
cb: (v: ObjectPick<T, K, V>) => RetWhen,
) => MatchRet<ObjectOmit<T, K, V>, K, ReturnDataType | RetWhen>;
verify: [T] extends [never]
? () => ReturnDataType
: { error: `missing '${OnlyString<T[K]>}'`; _type: never };
default: (cb: (value: T) => void) => void;
};
///// USAGE:
// type UserQuery =
// | { kind: 'list'; }
// | { kind: 'create'; data: NewUser }
// | { kind: 'update'; data: UpdateUser }
// | { kind: 'delete'; data: UserId }
// | { kind: 'DELETE'; data: UserId };
//
// const userQuery: UserQuery = await getNext();
//
// const logUserQuery = (uq: UserQuery) => {
// const logData = matchK(uq, 'type')
// .when('list', () => `listing users`)
// .when('create', (d) => `create user with id ${d.data.id}`)
// .when('update', (d) => `update user with id ${d.data.id}`)
// .when(['delete', 'DELETE'], (d) => `deleting user with id ${d.data}`)
// .verify();
//
// console.log(logData);
// }
export const matchK = <T extends Record<string, unknown>, K extends keyof T>(
data: T,
key: K,
): MatchRet<T, K, never> => {
const defaultV = (cb: (data: T) => void) => cb(data);
function when<V extends OnlyString<T[K]>, RetWhen>(
value: V | V[],
cb: (v: ObjectPick<T, K, V>) => void,
): MatchRet<ObjectOmit<T, K, V>, K, RetWhen> {
if (Array.isArray(value)) {
value.forEach((value) => when(value, cb));
return this;
}
if (this.foundMarker || data[key] !== value) return this;
const res = cb(data as ObjectPick<T, K, V>);
this.verify = () => res;
this.foundMarker = true;
this.default = () => {};
return this;
};
return ({ when, verify: () => undefined, default: defaultV }) as any;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment