Skip to content

Instantly share code, notes, and snippets.

@erikpukinskis
Last active December 18, 2023 17:44
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erikpukinskis/52e5e72f3625c89adc3b84e5b679deff to your computer and use it in GitHub Desktop.
Save erikpukinskis/52e5e72f3625c89adc3b84e5b679deff to your computer and use it in GitHub Desktop.
/**
* stripNulls
* https://gist.github.com/erikpukinskis/52e5e72f3625c89adc3b84e5b679deff
*
* Copyright 2023 Erik Pukinskis
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the “Software”), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/**
* Strips all object properties with null values. Returns a type where all of
* the nulls in the value union types are replaced with undefineds.
*
* For example, if you define:
*
* const foo: { a: number | null } = { a: null }
*
* Then `stripNulls(foo)` will return an empty object, and the type will be:
*
* { a: number | undefined }
*
* Works on deeply nested objects and arrays.
*/
export function stripNulls<T extends Nullable>(
nullable: T
): ReplaceNullWithUndefined<T> {
if (Array.isArray(nullable)) {
return nullable.map(stripNulls) as ReplaceNullWithUndefined<T>;
}
if (
typeof nullable === "object" &&
(nullable as Object).constructor === Object
) {
const withoutNulls: Record<string, unknown> = {};
for (const key in nullable) {
const value = nullable[key];
if (value != null) {
withoutNulls[key] = stripNulls(value);
}
}
return withoutNulls as ReplaceNullWithUndefined<T>;
}
return nullable as ReplaceNullWithUndefined<T>;
}
/**
* Takes an unknown type and gives you a type with the nulls swapped for undefines. Works for scalars, arrays,
* and objects:
*
* number | null => number | undefined
* (boolean | null)[] => (boolean | undefined)[]
* { a: string | null } => { a?: string}
*
* Also works recursively, so:
*
* { foo: (number | null)[] | null } => { foo?: (number | undefined)[] }
*/
export type ReplaceNullWithUndefined<T> = T extends null
? Exclude<T, null> | undefined
: T extends unknown[]
? SwapNullItemsForUndefined<T>
: T extends Record<string, unknown>
? OmitNullProperties<T>
: T;
type OmitNullProperties<ObjectType extends Record<string, unknown>> = {
[Key in keyof ObjectType]: ReplaceNullWithUndefined<ObjectType[Key]>;
};
type SwapNullItemsForUndefined<ArrayType extends unknown[]> =
ReplaceNullWithUndefined<ArrayType[number]>[];
/**
* Same as ReplaceNullWithUndefined, except in the reverse direction: converting any undefineds back to nulls:
*
* number | undefined => number | null
* (boolean | undefined)[] => (boolean | null)[]
* { a: string | undefined } => { a?: null}
*
*/
export type NullOut<T> = T extends undefined
? Exclude<T, undefined> | null
: T extends unknown[]
? NullOutItems<T>
: T extends Record<string, unknown>
? NullOutUndefinedProperties<T>
: T;
type NullOutUndefinedProperties<ObjectType extends Record<string, unknown>> = {
[Key in keyof ObjectType]: NullOut<ObjectType[Key]>;
};
type NullOutItems<ArrayType extends unknown[]> = NullOut<ArrayType[number]>[];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment