diff --git a/records/hasField.test.ts b/records/hasField.test.ts new file mode 100644 index 0000000..2bc65da --- /dev/null +++ b/records/hasField.test.ts @@ -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) +}) diff --git a/records/hasField.ts b/records/hasField.ts new file mode 100644 index 0000000..8544d4b --- /dev/null +++ b/records/hasField.ts @@ -0,0 +1,13 @@ +/** + * Checks if the given object has the specified field. + */ +export const hasField =
(obj: unknown, key: P): obj is Record
=>
+ 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
+ */
diff --git a/records/index.ts b/records/index.ts
new file mode 100644
index 0000000..5002ef4
--- /dev/null
+++ b/records/index.ts
@@ -0,0 +1,3 @@
+export * from './hasField.js'
+export * from './pickFields.js'
+export * from './omitFields.js'
diff --git a/records/omitFields.test.ts b/records/omitFields.test.ts
new file mode 100644
index 0000000..869b01f
--- /dev/null
+++ b/records/omitFields.test.ts
@@ -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)
+})
diff --git a/records/omitFields.ts b/records/omitFields.ts
new file mode 100644
index 0000000..cdeea54
--- /dev/null
+++ b/records/omitFields.ts
@@ -0,0 +1,19 @@
+/**
+ * Omits specified fields from an object and returns a copy without those fields.
+ */
+export const omitFields =