From e3b317ecdd76092b49917553b86ec2d83b7edfc2 Mon Sep 17 00:00:00 2001 From: Travis Fischer Date: Thu, 1 Aug 2024 01:41:36 -0500 Subject: [PATCH] feat: add pruneEmptyDeep; improve omit types --- legacy/src/utils.test.ts | 73 ++++++++++++++++++++++++++++++++++++++-- legacy/src/utils.ts | 52 +++++++++++++++++++++++++--- legacy/tsconfig.json | 4 --- 3 files changed, 118 insertions(+), 11 deletions(-) diff --git a/legacy/src/utils.test.ts b/legacy/src/utils.test.ts index d3c45c79..cc1c45d0 100644 --- a/legacy/src/utils.test.ts +++ b/legacy/src/utils.test.ts @@ -6,6 +6,8 @@ import { mockKyInstance } from './_utils' import { omit, pick, + pruneEmpty, + pruneEmptyDeep, sanitizeSearchParams, stringifyForModel, throttleKy @@ -20,9 +22,11 @@ test('pick', () => { test('omit', () => { expect(omit({ a: 1, b: 2, c: 3 }, 'a', 'c')).toEqual({ b: 2 }) - expect( - omit({ a: { b: 'foo' }, d: -1, foo: null } as any, 'b', 'foo') - ).toEqual({ a: { b: 'foo' }, d: -1 }) + expect(omit({ a: { b: 'foo' }, d: -1, foo: null }, 'b', 'foo')).toEqual({ + a: { b: 'foo' }, + d: -1 + }) + expect(omit({ a: 1, b: 2, c: 3 }, 'foo', 'bar', 'c')).toEqual({ a: 1, b: 2 }) }) test('sanitizeSearchParams', () => { @@ -71,6 +75,69 @@ describe('stringifyForModel', () => { }) }) +test('pruneEmpty', () => { + expect( + pruneEmpty({ + a: 1, + b: { foo: true }, + c: [true], + d: 'foo', + e: null, + f: undefined + }) + ).toEqual({ + a: 1, + b: { foo: true }, + c: [true], + d: 'foo' + }) + + expect(pruneEmpty({ a: 0, b: {}, c: [], d: '' })).toEqual({ + a: 0 + }) + expect(pruneEmpty({ b: {}, c: [], d: '' })).toEqual({}) + + expect( + pruneEmpty({ + a: null, + b: { foo: [{}], bar: [null, undefined, ''] }, + c: ['', '', ''], + d: '', + e: undefined, + f: [], + g: {} + }) + ).toEqual({ + b: { foo: [{}], bar: [null, undefined, ''] }, + c: ['', '', ''] + }) +}) + +test('pruneEmptyDeep', () => { + expect( + pruneEmptyDeep({ a: 1, b: { foo: true }, c: [true], d: 'foo' }) + ).toEqual({ + a: 1, + b: { foo: true }, + c: [true], + d: 'foo' + }) + + expect(pruneEmptyDeep({ a: 0, b: {}, c: [], d: '' })).toEqual({ + a: 0 + }) + + expect( + pruneEmptyDeep({ + a: null, + b: { foo: [{}], bar: [null, undefined, ''] }, + c: ['', '', ''], + d: '', + e: undefined + }) + ).toEqual(undefined) +}) + test( 'throttleKy should rate-limit requests to ky properly', async () => { diff --git a/legacy/src/utils.ts b/legacy/src/utils.ts index 0e4601e6..5acef099 100644 --- a/legacy/src/utils.ts +++ b/legacy/src/utils.ts @@ -15,8 +15,8 @@ export { default as delay } from 'delay' * ``` */ export const omit = < - T extends Record | object, - K extends keyof T = keyof T + T extends Record | object, + K extends keyof any >( inputObj: T, ...keys: K[] @@ -36,8 +36,8 @@ export const omit = < * ``` */ export const pick = < - T extends Record | object, - K extends keyof T = keyof T + T extends Record | object, + K extends keyof T >( inputObj: T, ...keys: K[] @@ -110,6 +110,50 @@ export function pruneEmpty>( ) as NonNullable } +export function pruneEmptyDeep( + value?: T +): + | undefined + | (T extends Record + ? { [K in keyof T]: Exclude } + : T extends Array + ? Array> + : Exclude) { + if (value === undefined || value === null) return undefined + + if (typeof value === 'string') { + if (!value) return undefined + + return value as any + } + + if (Array.isArray(value)) { + if (!value.length) return undefined + + value = value + .map((v) => pruneEmptyDeep(v)) + .filter((v) => v !== undefined) as any + + if (!value || !Array.isArray(value) || !value.length) return undefined + return value as any + } + + if (typeof value === 'object') { + if (!Object.keys(value).length) return undefined + + value = Object.fromEntries( + Object.entries(value) + .map(([k, v]) => [k, pruneEmptyDeep(v)]) + .filter(([, v]) => v !== undefined) + ) + + if (!value || !Object.keys(value).length) return undefined + return value as any + } + + return value as any +} + export function getEnv(name: string): string | undefined { try { return typeof process !== 'undefined' diff --git a/legacy/tsconfig.json b/legacy/tsconfig.json index 36549d9a..8d623777 100644 --- a/legacy/tsconfig.json +++ b/legacy/tsconfig.json @@ -22,10 +22,6 @@ "strict": true, "useDefineForClassFields": true, "verbatimModuleSyntax": true - - // NOTE: these are deprecated - // "experimentalDecorators": true, - // "emitDecoratorMetadata": true, }, "include": ["src"] }