Skip to content

Commit 9d3a10b

Browse files
authored
Typed object from entries (#53)
1 parent 8d50097 commit 9d3a10b

File tree

5 files changed

+32
-3
lines changed

5 files changed

+32
-3
lines changed

.changeset/smooth-news-deliver.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'emery': patch
3+
---
4+
5+
Add `typedObjectFromEntries` utility, and improve `ObjectEntry` type.

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export {
3434
export { castToOpaque } from './opaques';
3535

3636
export { getErrorMessage } from './utils/error';
37-
export { typedEntries, typedKeys } from './utils/object';
37+
export { typedEntries, typedKeys, typedObjectFromEntries } from './utils/object';
3838

3939
// Types
4040
// ------------------------------

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
export type ErrorLike = { message: string };
77

8-
export type ObjectEntry<T> = { [K in keyof T]: [K, T[K]] }[keyof T];
8+
export type ObjectEntry<T> = { [K in keyof T]-?: [K, T[K]] }[keyof T];
99

1010
export type Nullish = null | undefined;
1111

src/utils/object.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { typedEntries, typedKeys } from './object';
1+
import { typedEntries, typedKeys, typedObjectFromEntries } from './object';
22

33
describe('utils/object', () => {
44
describe('typedEntries', () => {
@@ -16,4 +16,13 @@ describe('utils/object', () => {
1616
expect(result).toEqual(['foo', 'bar']);
1717
});
1818
});
19+
describe('typedObjectFromEntries', () => {
20+
it('should return the correct object', () => {
21+
const result = typedObjectFromEntries([
22+
['foo', 1],
23+
['bar', 2],
24+
]);
25+
expect(result).toEqual({ foo: 1, bar: 2 });
26+
});
27+
});
1928
});

src/utils/object.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,18 @@ export function typedEntries<T extends object>(value: T) {
2121
export function typedKeys<T extends object>(value: T) {
2222
return Object.keys(value) as Array<keyof T>;
2323
}
24+
25+
/**
26+
* An alternative to `Object.fromEntries()` that avoids type widening. Must be
27+
* used in conjunction with `typedEntries` or `typedKeys`.
28+
*
29+
* @example
30+
* const obj = { name: 'Alice', age: 30 };
31+
* const rebuilt1 = Object.fromEntries(Object.entries(obj));
32+
* // ^? { [k: string]: string | number }
33+
* const rebuilt2 = typedObjectFromEntries(typedEntries(obj));
34+
* // ^? { name: string, age: number }
35+
*/
36+
export function typedObjectFromEntries<T extends object>(entries: ObjectEntry<T>[]) {
37+
return Object.fromEntries(entries) as T;
38+
}

0 commit comments

Comments
 (0)