Last active
January 4, 2022 09:14
-
-
Save sidola/26b946557262f8f9323fad33d0c068a1 to your computer and use it in GitHub Desktop.
Make function properties optional
This file contains hidden or 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
/* | |
Why are we using Exclude<..., undefined>? Because when extracting | |
keys, optional properties will resolve to undefined. See here: | |
https://github.com/microsoft/TypeScript/issues/34992#issuecomment-551822423 | |
--- | |
The Optional/Required types below are leveraging the fact that: | |
{ a?: string, b: string } extends { a: string } => false | |
{ a?: string, b: string } extends { b: string } => true | |
Meaning, for each key:type pair, we create a Record type, then | |
check if the wider type will extend it. Since we can never extend | |
a required property into an optional, we have a way to detect | |
optional properties. | |
*/ | |
type OptionalKeys<T> = Exclude<{ [K in keyof T]: T extends Record<K, T[K]> ? never : K }[keyof T], undefined> | |
type RequiredKeys<T> = Exclude<{ [K in keyof T]: T extends Record<K, T[K]> ? K : never }[keyof T], undefined> | |
type FunctionKeys<T> = Exclude<{ [K in keyof T]: T[K] extends Function ? K : never }[keyof T], undefined> | |
type NonFunctionKeys<T> = Exclude<{ [K in keyof T]: T[K] extends Function ? never : K }[keyof T], undefined> | |
export type FunctionsPartial<Type> = | |
// These types we just handle as-is, and send their original type | |
// back out. | |
Type extends Function | any[] | string | number | boolean | undefined | |
? Type | |
// When we encounter objects, we will create a new type, where we | |
// move all function properties to optional keys. Notice that we | |
// also have to deal with all previously optional keys, otherwise | |
// we'd make everything EXCEPT functions required. | |
: Type extends object | |
? ( | |
{ [Key in OptionalKeys<Type> | FunctionKeys<Type>]+?: FunctionsPartial<Type[Key]> } & | |
{ [Key in RequiredKeys<Type> & NonFunctionKeys<Type>]-?: FunctionsPartial<Type[Key]> } | |
) | |
// Reaching this point is a no-op, as all types should've been exhausted above. | |
: "¤function-partials-is-broken" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment