From 6f2e6c713b01f669dfe776538517a17727f6d317 Mon Sep 17 00:00:00 2001 From: jossmac <2730833+jossmac@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:53:26 +1000 Subject: [PATCH 1/3] improve `ObjectEntry` type --- src/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.ts b/src/types.ts index f405b5f..a11e51c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,7 +5,7 @@ export type ErrorLike = { message: string }; -export type ObjectEntry = { [K in keyof T]: [K, T[K]] }[keyof T]; +export type ObjectEntry = { [K in keyof T]-?: [K, T[K]] }[keyof T]; export type Nullish = null | undefined; From d9eec3a310f757025b842304ec3a9cb37822c500 Mon Sep 17 00:00:00 2001 From: jossmac <2730833+jossmac@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:53:38 +1000 Subject: [PATCH 2/3] add `typedObjectFromEntries` utility --- src/index.ts | 2 +- src/utils/object.test.ts | 11 ++++++++++- src/utils/object.ts | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index ac584b6..3ad49da 100644 --- a/src/index.ts +++ b/src/index.ts @@ -34,7 +34,7 @@ export { export { castToOpaque } from './opaques'; export { getErrorMessage } from './utils/error'; -export { typedEntries, typedKeys } from './utils/object'; +export { typedEntries, typedKeys, typedObjectFromEntries } from './utils/object'; // Types // ------------------------------ diff --git a/src/utils/object.test.ts b/src/utils/object.test.ts index ce8ce73..aeeced4 100644 --- a/src/utils/object.test.ts +++ b/src/utils/object.test.ts @@ -1,4 +1,4 @@ -import { typedEntries, typedKeys } from './object'; +import { typedEntries, typedKeys, typedObjectFromEntries } from './object'; describe('utils/object', () => { describe('typedEntries', () => { @@ -16,4 +16,13 @@ describe('utils/object', () => { expect(result).toEqual(['foo', 'bar']); }); }); + describe('typedObjectFromEntries', () => { + it('should return the correct object', () => { + const result = typedObjectFromEntries([ + ['foo', 1], + ['bar', 2], + ]); + expect(result).toEqual({ foo: 1, bar: 2 }); + }); + }); }); diff --git a/src/utils/object.ts b/src/utils/object.ts index 966a81a..42a3d07 100644 --- a/src/utils/object.ts +++ b/src/utils/object.ts @@ -21,3 +21,18 @@ export function typedEntries(value: T) { export function typedKeys(value: T) { return Object.keys(value) as Array; } + +/** + * An alternative to `Object.fromEntries()` that avoids type widening. Must be + * used in conjunction with `typedEntries` or `typedKeys`. + * + * @example + * const obj = { name: 'Alice', age: 30 }; + * const rebuilt1 = Object.fromEntries(Object.entries(obj)); + * // ^? { [k: string]: string | number } + * const rebuilt2 = typedObjectFromEntries(typedEntries(obj)); + * // ^? { name: string, age: number } + */ +export function typedObjectFromEntries(entries: ObjectEntry[]) { + return Object.fromEntries(entries) as T; +} From f05be682ecad652a17f2cb34ec01e452e4644142 Mon Sep 17 00:00:00 2001 From: jossmac <2730833+jossmac@users.noreply.github.com> Date: Tue, 8 Apr 2025 08:56:06 +1000 Subject: [PATCH 3/3] add changeset --- .changeset/smooth-news-deliver.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/smooth-news-deliver.md diff --git a/.changeset/smooth-news-deliver.md b/.changeset/smooth-news-deliver.md new file mode 100644 index 0000000..2de1ec0 --- /dev/null +++ b/.changeset/smooth-news-deliver.md @@ -0,0 +1,5 @@ +--- +'emery': patch +--- + +Add `typedObjectFromEntries` utility, and improve `ObjectEntry` type.