Skip to content

Commit

Permalink
add record extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
andrej-dyck committed Dec 16, 2023
1 parent e8e7f01 commit 7409b60
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 0 deletions.
16 changes: 16 additions & 0 deletions records/hasField.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { expect, test } from 'vitest'
import { hasField } from './hasField.js'

test('hasField infers that a field exists on an unknown object', () => {
const obj: unknown = { id: 1 }
expect(hasField(obj, 'id') ? obj.id : 0).toBe(1)
})

test.each([
{ obj: { a: 1 }, key: 'a', expected: true },
{ obj: { a: 1 }, key: 'b', expected: false },
{ obj: {}, key: 'a', expected: false },
{ obj: undefined, key: 'a', expected: false },
])('hasField checks that a field exists on an unknown object', ({ obj, key, expected }) => {
expect(hasField(obj, key)).toBe(expected)
})
13 changes: 13 additions & 0 deletions records/hasField.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* Checks if the given object has the specified field.
*/
export const hasField = <P extends PropertyKey>(obj: unknown, key: P): obj is Record<P, unknown> =>
obj != null && typeof obj === 'object' && key in obj

/*
* Example:
* const obj: { data: unknown } | { error: string } | unknown = ...
* hasField(obj, 'data')
* ? JSON.stringify(obj.data) // inferred that the data field exists
* : obj?.error // inferred that it's nullable or has error field
*/
3 changes: 3 additions & 0 deletions records/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './hasField.js'
export * from './pickFields.js'
export * from './omitFields.js'
15 changes: 15 additions & 0 deletions records/omitFields.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expect, test } from 'vitest'
import { omitFields } from './omitFields.js'

test('omitFields omits a field from an object', () => {
expect(omitFields({ a: 1, b: 2, c: 3 }, 'b')).toMatchObject({ a: 1, c: 3 })
})

test('omitFields omits multiple fields from an object', () => {
expect(omitFields({ a: 1, b: 2, c: 3 }, 'b', 'c')).toMatchObject({ a: 1 })
})

test('omitFields returns same obj if no keys are specified', () => {
const obj = { a: 1, b: 2, c: 3 }
expect(omitFields(obj)).toBe(obj)
})
19 changes: 19 additions & 0 deletions records/omitFields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Omits specified fields from an object and returns a copy without those fields.
*/
export const omitFields = <T extends Record<PropertyKey, unknown>, P extends keyof T>(obj: T, ...keys: P[]): Omit<T, P> => {
if (keys.length === 0) return obj

const copy = { ...obj }
for (const k of keys) {
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete copy[k]
}
return copy
}

/*
* Example:
* const order = { id: ..., date: ..., customer: ..., status: ..., items: [...] }
* const result = omitFields(order, 'customer', 'status') // { id: ..., date: ..., items: [...] }
*/
15 changes: 15 additions & 0 deletions records/pickFields.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { expect, test } from 'vitest'
import { pickFields } from './pickFields.js'

test('pickFields picks a field from an object', () => {
expect(pickFields({ a: 1, b: 2, c: 3 }, 'b')).toMatchObject({ b: 2 })
})

test('pickFields picks multiple fields from an object', () => {
expect(pickFields({ a: 1, b: 2, c: 3 }, 'a', 'b')).toMatchObject({ a: 1, b: 2 })
})

test('pickFields returns same obj if no keys are specified', () => {
const obj = { a: 1, b: 2, c: 3 }
expect(pickFields(obj)).toBe(obj)
})
18 changes: 18 additions & 0 deletions records/pickFields.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Picks specific fields from an object and returns a copy with only those fields.
*/
export const pickFields = <T extends Record<PropertyKey, unknown>, P extends keyof T>(obj: T, ...keys: P[]): Pick<T, P> => {
if (keys.length === 0) return obj

const copy = {} as Pick<T, P>
for (const k of keys) {
copy[k] = obj[k]
}
return copy
}

/*
* Example:
* const order = { id: ..., date: ..., customer: ..., status: ..., items: [...] }
* const result = pickFields(order, 'id', 'date') // { id: ..., date: ... }
*/

0 comments on commit 7409b60

Please sign in to comment.