Skip to content

Commit

Permalink
feat: add pruneEmptyDeep; improve omit types
Browse files Browse the repository at this point in the history
  • Loading branch information
transitive-bullshit committed Aug 1, 2024
1 parent 08012e2 commit 84544c7
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 11 deletions.
73 changes: 70 additions & 3 deletions src/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { mockKyInstance } from './_utils'
import {
omit,
pick,
pruneEmpty,
pruneEmptyDeep,
sanitizeSearchParams,
stringifyForModel,
throttleKy
Expand All @@ -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', () => {
Expand Down Expand Up @@ -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 () => {
Expand Down
52 changes: 48 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export { default as delay } from 'delay'
* ```
*/
export const omit = <
T extends Record<any, unknown> | object,
K extends keyof T = keyof T
T extends Record<string, unknown> | object,
K extends keyof any
>(
inputObj: T,
...keys: K[]
Expand All @@ -36,8 +36,8 @@ export const omit = <
* ```
*/
export const pick = <
T extends Record<any, unknown> | object,
K extends keyof T = keyof T
T extends Record<string, unknown> | object,
K extends keyof T
>(
inputObj: T,
...keys: K[]
Expand Down Expand Up @@ -110,6 +110,50 @@ export function pruneEmpty<T extends Record<string, any>>(
) as NonNullable<T>
}

export function pruneEmptyDeep<T>(
value?: T
):
| undefined
| (T extends Record<string, any>
? { [K in keyof T]: Exclude<T[K], undefined | null> }
: T extends Array<infer U>
? Array<Exclude<U, undefined | null>>
: Exclude<T, null>) {
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'
Expand Down
4 changes: 0 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@
"strict": true,
"useDefineForClassFields": true,
"verbatimModuleSyntax": true

// NOTE: these are deprecated
// "experimentalDecorators": true,
// "emitDecoratorMetadata": true,
},
"include": ["src"]
}

0 comments on commit 84544c7

Please sign in to comment.