Skip to content

Instantly share code, notes, and snippets.

@dmorosinotto
Last active May 28, 2020 07:00
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 dmorosinotto/706a453c69e8797373d89a960124d8d6 to your computer and use it in GitHub Desktop.
Save dmorosinotto/706a453c69e8797373d89a960124d8d6 to your computer and use it in GitHub Desktop.
Unionize<T> / Objectify<T> - TypeScript utility helpers to map from Object --> KeyValue Par {k: K , v: V} --> Object
// Utility types to map from Object --(Unionize)--> KeyValue Pair {k: K, v: V} --(Objectify)--> back to Object
// helpful in case where you need to "tasform/filter/extract" some prop in the Object using Conditional Types
// ORIGINAL CODE + READ MORE INFO/SAMPLE USE CASES: https://effectivetypescript.com/2020/05/12/unionize-objectify/
type Unionize<T extends object> = {
[k in keyof T]: {k: k; v: T[k]}
}[keyof T];
// type PropertyKey = keyof any = string | number | symbol
type KVPair = {k: PropertyKey; v: unknown}
type Objectify<T extends KVPair> = {
[k in T['k']]: Extract<T, {k: k}>['v']
};
// HELPFULL USE CASES - ORIGINAL CODE + READ MORE INFO: https://effectivetypescript.com/2020/05/12/unionize-objectify/
type OmitKV<T extends KVPair, V> = T extends {v: V} ? never : T;
type OmitProperties<T extends object, V> = Objectify<OmitKV<Unionize<T>, V>>;
type PickKV<T extends KVPair, V> = T extends {v: V} ? T : never;
type PickProperties<T extends object, V> = Objectify<PickKV<Unionize<T>, V>>;
type PickKeys<T extends object, V> = Extract<Unionize<T>, {v: V}>['k'];
type OmitKeys<T extends object, V> = Exclude<Unionize<T>, {v: V}>['k'];
// More correct type defintion for lodash _.invert
type SwapKV<T> =
T extends {k: infer K, v: infer V}
? V extends PropertyKey // <-- additional PropertyKey constraint
? {k: V; v: K; } // <-- note the swap!
: never
: never;
type Inverted<T extends object> = Objectify<SwapKV<Unionize<T>>>;
// SAMPLES - ORIGINAL CODE + READ MORE INFO: https://effectivetypescript.com/2020/05/12/unionize-objectify/
type Person = {
name: string,
title: string,
age: number,
dob: Date | null,
greet: ()=> string
}
type PersonData = OmitProperties<Person, Function> // type { name: string, title: string, age: number, dob: Date | null }
type PersonMethods = PickProperties<Person, Function> // type { great: ()=> string }
type KeysOfPersonString = PickKeys<Person, string> // "name" | "title"
function indexByStringField<T extends object>(obj: T[], field: PickKeys<T, string>) { /* ... */ }
// THE MOST CORRECT JSON DEFINTION I EVER FOUND
type Jsonify<T> = T extends {toJSON(): infer U}
? U
: T extends Array<infer U>
? Array<Jsonify<U>>
: T extends object
? {
[P in OmitKeys<T, Function>]: Jsonify<T[P]>;
}
: T;
type PersonDTO = Jsonify<Person>; // type { name: string, title: string, age: number, dob: string | null }
const size = { S: "small", L: "large" } as const;
type Dsize = Inverted<typeof size>; // type { small: "S", large: "L" }
-> { small: "S", large: "L" }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment