Created
July 5, 2022 07:45
-
-
Save sidola/8b403e69e1513f9443fa761d1af5c968 to your computer and use it in GitHub Desktop.
Ensures a given property is defined and changes its type accordingly.
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
/** | |
* Creates a new type where prop T on object K is required and | |
* non-nullable. | |
*/ | |
type DefinedProp<T, K extends keyof T> = ( | |
Omit<T, K> & | |
{ [key in K]-?: Exclude<T[key], undefined> } | |
) | |
/** | |
* For the given object and key, ensures the property at the given key | |
* is defined and forces the type to reflect that. | |
* | |
* It's basically a nested `assertDefined` with a return. If there's | |
* an easier way to get TS to remember scoped null checks on nested | |
* properties I'd like to know about it. | |
* | |
* @example | |
* type City = { street: string, zip: number } | |
* | |
* // Notice 'city' is optional, we're going to ensure that is defined | |
* type Data = { name: string, city?: City | undefined } | |
* | |
* const data: Data = { | |
* name: 'Bob', | |
* city: { | |
* street: 'Some street', | |
* zip: 12345 | |
* } | |
* } | |
* | |
* const withCityDefined = assumeDefined(data, 'city') | |
* | |
* // 'city' is guaranteed to be defined here | |
* console.log(withCityDefined.city.street) | |
*/ | |
export function assumeDefined<T, K extends keyof T>(obj: T, key: K): DefinedProp<T, K> { | |
if (obj[key] == null) { | |
throw Error(`Property ${String(key)} in ${JSON.stringify(obj)} was not defined`) | |
} | |
return obj as unknown as DefinedProp<T, K> | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment