Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gvergnaud/d79c00b61e383bd5e7e7ced2ff59d295 to your computer and use it in GitHub Desktop.
Save gvergnaud/d79c00b61e383bd5e7e7ced2ff59d295 to your computer and use it in GitHub Desktop.
Can you express any mapped types in terms of recursive + distributive union type?
// mapped type version
type Fn<T> = {
[K in keyof T]: [K, T[K]] /* arbitrary fn using K and T[k] */;
};
// distributive union version
type Fn2<T> = FromEntries<
keyof T extends infer K
? K extends keyof T
? [K, [K, T[K]] /* arbitrary fn using K and T[k] */]
: never
: never
>;
type UnionToIntersection<T> = (T extends any ? (p: T) => void : never) extends (
p: infer I
) => void
? I
: never;
type Compute<T> = { [K in keyof T]: T[K] } & unknown;
type FromEntries<Entry extends [PropertyKey, unknown]> = Compute<
UnionToIntersection<Entry extends any ? Record<Entry[0], Entry[1]> : never>
// ^
// This is cheating a bit because Record is defined as a simple map type.
>;
type res1 = Fn<{ age: number; name: string }>;
type test1 = Expect<
Equal<res1, { age: ["age", number]; name: ["name", string] }>
>;
type res2 = Fn2<{ age: number; name: string }>;
type test2 = Expect<
Equal<res2, { age: ["age", number]; name: ["name", string] }>
>;
/**
* Secret Helpers! 🤫
*/
/**
* `Equal` takes 2 types and returns `true` if they are the same.
*/
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
T
>() => T extends Y ? 1 : 2
? true
: false;
/**
* `Expect` takes one type parameter and only typechecks if it is true.
*/
type Expect<T extends true> = T;
// Playground: https://www.typescriptlang.org/play?#code/C4TwDgpgBAYgdgHgCoD4oF4oG8BQUoDaA0lAJZxQDWEIA9gGZRIC6AXIUQDRPHPNQB6AFRQAhgCcARqWDiJIKPQoBXAM7kA5lBKi4AEx6V+UIQIDcOAL4WcAgVACaEVd0nLgZYAHJVYqAAtRVVUAGwhWHFBIWDgAJmQ0TBhxWgBbAFE4WVJnBDwqGgYmKAgAD2AIfV9yeghxbXz8AH5tEvLKvV9qOkYkRvwoFuJuYZ4iPkERCWlZeUUVdTgtHX1DY1Nmfvx2OAgANzr+nf3DlBso6ABVOFJaOCRaAEksutUIAGNgW8RUDCgACiQbQqVTEcAULX+YHYSAAlBg0HtaKQDMcDuJ4WUQZ0AfloWQ4LV6o8cPD0IjkXp8i0SdsoLt0edwNAAMJpMDuCAJP5YDgEgo9JhsMb8SxQABkUGUcEocFoAHc4Ezosk0plsrl1eIFFiOr4CAAFFKQcSgIg0bjS2UKuDMRJQNmpDkVPL4a7fB7PCriN6fb4ILU69qg3QQqAAJQ+tHEegDWW1BAADMxuIGCABGO1QNF1FD5OwDQtF4sl0tFgB6+fsRaQ-lI1V8738EFEXyWfhmUEkH1Eamgkfe0YM9agegg9HIEAMQT86idYSgqVEYCgFwAdDgzjhIsyoOJnOm-vAELzRBpwvTlKlu+IzPTRKkL6psu3LFuLqvnMBD5h0qVIJ8rpQOkACOyiiCECD7qo6bcKe57sAQABEZ4QEh3BwFeN7MHecAPheyF4Y+6FQM+4iaKKeZbju0TQbER5xCeYgIZe151Lh+HsGRmhQG+FgfhUz70b+-4fMAQGgeBkF0XBzEESh54kZhbHiDh96PohSFEWh3DcUslGbjYthCEIeAiAAyh8+4eAAEhAIQmqoACEUCAHwbgDUu2ZAjbsIpkmFAAAGkkQQFq6iNQvj0Rcvi6AY1nKOIcC+AFsjKBAoWkIwwDNgoEjQNl0CqPhG4mN5H7BZBAAa3AOPa-wJP8ZJoECuqgpVgxQIe7CxJiwY4kBfQoI1CLFK1OIOB1XVQLE1KruIaX5Ow9AQW8Ni+WZgV-gBwChcA4XOFAdz5buYASPh3pggYdwhAoFxNh8lDVIwMhkL4qUQCVpg0dAW1icgwJ6nNaX2kgFhAA
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment