Last active
November 14, 2022 14:26
-
-
Save dmorosinotto/d70f0e547010c4706cc6f02eb07c6a46 to your computer and use it in GitHub Desktop.
TS Helpers: Mapped to trasform a Type copying only same props and forcing requied/optionals + Keep & Extract from Object only props of a specific Type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//EXPERIMENTS WITH TYPESCRIPT UTILITY TYPES: https://www.typescriptlang.org/docs/handbook/utility-types.html | |
//AND MAPPED TYPES: https://mariusschulz.com/blog/mapped-types-in-typescript | |
//READ MORE HERE: https://blog.logrocket.com/mastering-mapped-types-typescript/ | |
type KeepOnlyPropOfT<O, T> = { | |
[K in keyof O]: O[K] extends T ? K : never | |
}[keyof O] | |
type ExtractOnlyT<O, T> = { | |
[P in KeepOnlyPropOfT<O,T>]: O[P] | |
} | |
//INSPIRED BY https://stackoverflow.com/questions/53899692/typescript-how-to-extract-only-the-optional-keys-from-a-type | |
type OptionalPropertyOf<T extends object> = Exclude<{ | |
[K in keyof T]: T extends Record<K, T[K]> | |
? never | |
: K | |
}[keyof T], undefined> | |
//CONCEPTUALLY SIMILAR TO Omit<T,E> & Required<Pick<T,R>> & Partial<Pick<T,O>> BUT WITHOUT CONFLICTS REDEFINING PROPS | |
type Mapped<T extends object, Required extends keyof T, Optionals extends keyof T, Excluded extends keyof T> | |
= { [P in Required | Exclude<keyof T, Excluded | OptionalPropertyOf<T> | Optionals>]-?: Exclude<T[P],undefined> } //COPYING ORIGINAL PROPS OMIT Excluded + FORCE -? FOR Required! | |
& { [P in Optionals | Exclude<OptionalPropertyOf<T>, Excluded | Required>]?: T[P] } //FORCE +? FOR Optionals + TRICK TO KEEP ORIGINAL OPTIONALS PROPS? NOT (Excluded | Required)! | |
//SAMPLE USE CASE | |
type Party = { | |
name: string, | |
partecipants?: number, | |
location?: { | |
address: string, | |
city: string | |
}, | |
ended: boolean, | |
selected: boolean, | |
} | |
type boolProps = KeepOnlyPropOfT<Party, boolean> // "ended" | "selected" | |
type stringProps = KeepOnlyPropOfT<Party, string> // "name" | |
type flags = ExtractOnlyT<Party, boolean> // { ended: boolean, selected: boolean } | |
type strings = ExtractOnlyT<Party, string> // { name: string } | |
type soloBoolProps<T> = KeepOnlyPropOfT<T, boolean> | |
type xx = soloBoolProps<Party> // "ended" | "selected" | |
function getBoolProp<T>(obj: T, prop: soloBoolProps<T>){ | |
return obj[prop] | |
} | |
const party: Party = { name: "TS ROCKS!!!", ended: false, selected: true } | |
let e = getBoolProp(party,"ended") //boolean OK -> false | |
let s = getBoolProp(party,"selected") //boolean OK -> true | |
let ERROR = getBoolProp(party,"name") //TS ERROR NOT VALID PROPS!!! | |
// ^^^^^ Argument of type "name" is not assignable to soloBoolProps<Party> | |
type Opts = OptionalPropertyOf<Party>; // "partecipants" | "location" | |
type Copy = Mapped<Party, never, never, never> //IS THE SAMPE AS Party with partition from ORIGINALS required and optionals | |
let c: Copy = party; //OK same type no problem | |
type maybeSelPartecipant = Mapped<Party, "partecipants", "selected", "name" | "location" | "ended"> | |
let ok: maybeSelPartecipant = { partecipants: 42 }; ok.selected = true; //OK ONLY partecipants IS REQUIERED + selcted OPTIONAL | |
let ko: maybeSelPartecipant = party; //ERROR SHAPE DON'T MACTCH partecipants IN Party CAN BE undefined BUT HERE IS REQUIRED!!! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment