Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ export type {FixedLengthArray} from './source/fixed-length-array.d.ts';
export type {MultidimensionalArray} from './source/multidimensional-array.d.ts';
export type {MultidimensionalReadonlyArray} from './source/multidimensional-readonly-array.d.ts';
export type {IterableElement} from './source/iterable-element.d.ts';
export type {Entry} from './source/entry.d.ts';
export type {Entries} from './source/entries.d.ts';
export type {Entry, ObjectEntry} from './source/entry.d.ts';
export type {Entries, ObjectEntries} from './source/entries.d.ts';
export type {SetReturnType} from './source/set-return-type.d.ts';
export type {SetParameterType} from './source/set-parameter-type.d.ts';
export type {Asyncify} from './source/asyncify.d.ts';
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,9 @@ Click the type names for complete docs.
- [`Stringified`](source/stringified.d.ts) - Create a type with the keys of the given type changed to `string` type.
- [`IterableElement`](source/iterable-element.d.ts) - Get the element type of an `Iterable`/`AsyncIterable`. For example, `Array`, `Set`, `Map`, generator, stream, etc.
- [`Entry`](source/entry.d.ts) - Create a type that represents the type of an entry of a collection.
- [`ObjectEntry`](source/entry.d.ts) - Create a type that represents the type of an entry of an object type.
- [`Entries`](source/entries.d.ts) - Create a type that represents the type of the entries of a collection.
- [`ObjectEntries`](source/entries.d.ts) - Create a type that represents the type of the entries of an object type.
- [`SetReturnType`](source/set-return-type.d.ts) - Create a function type with a return type of your choice and the same parameters as the given function type.
- [`SetParameterType`](source/set-parameter-type.d.ts) - Create a function that replaces some parameters with the given parameters.
- [`Simplify`](source/simplify.d.ts) - Useful to flatten the type output to improve type hints shown in editors. And also to transform an interface into a type to aide with assignability.
Expand Down
28 changes: 26 additions & 2 deletions source/entries.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,34 @@
import type {_ArrayEntry, _MapEntry, _ObjectEntry, _SetEntry} from './entry.d.ts';
import type {_ArrayEntry, _MapEntry, _SetEntry, ObjectEntry} from './entry.d.ts';

type ArrayEntries<BaseType extends readonly unknown[]> = Array<_ArrayEntry<BaseType>>;
type MapEntries<BaseType> = Array<_MapEntry<BaseType>>;
type ObjectEntries<BaseType> = Array<_ObjectEntry<BaseType>>;
type SetEntries<BaseType extends Set<unknown>> = Array<_SetEntry<BaseType>>;

/**
Create a type that represents the type of the entries of an object type.

This is useful when you want to work with `Object.entries()` directly without going through the generic {@link Entries} type, which may lose type information with generic type parameters due to TypeScript's conditional type limitations.

@see {@link ObjectEntry} for the type of a single entry.
@see {@link Entries} for a version that works with all collection types.

@example
```
import type {ObjectEntries} from 'type-fest';

type Example = {
a: number;
b: string;
};

type ExampleEntries = ObjectEntries<Example>;
//=> ObjectEntry<Example>[]
```

@category Object
*/
export type ObjectEntries<BaseType> = Array<ObjectEntry<BaseType>>;

/**
Many collections have an `entries` method which returns an array of a given object's own enumerable string-keyed property [key, value] pairs. The `Entries` type will return the type of that collection's entries.

Expand Down
27 changes: 25 additions & 2 deletions source/entry.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,30 @@ type MapValue<BaseType> = BaseType extends Map<unknown, infer ValueType> ? Value

export type _ArrayEntry<BaseType extends readonly unknown[]> = [number, BaseType[number]];
export type _MapEntry<BaseType> = [MapKey<BaseType>, MapValue<BaseType>];
export type _ObjectEntry<BaseType> = [keyof BaseType, BaseType[keyof BaseType]];
/**
Create a type that represents the type of an entry of an object type.

This is useful when you want to work with `Object.entries()` directly without going through the generic {@link Entry} type, which may lose type information with generic type parameters due to TypeScript's conditional type limitations.

@see {@link ObjectEntries} for the array of all entries.
@see {@link Entry} for a version that works with all collection types.

@example
```
import type {ObjectEntry} from 'type-fest';

type Example = {
a: number;
b: string;
};

type ExampleEntry = ObjectEntry<Example>;
//=> [keyof Example, number | string]
```

@category Object
*/
export type ObjectEntry<BaseType> = [keyof BaseType, BaseType[keyof BaseType]];
export type _SetEntry<BaseType> = BaseType extends Set<infer ItemType> ? [ItemType, ItemType] : never;

/**
Expand Down Expand Up @@ -61,7 +84,7 @@ export type Entry<BaseType> =
BaseType extends Map<unknown, unknown> ? _MapEntry<BaseType>
: BaseType extends Set<unknown> ? _SetEntry<BaseType>
: BaseType extends readonly unknown[] ? _ArrayEntry<BaseType>
: BaseType extends object ? _ObjectEntry<BaseType>
: BaseType extends object ? ObjectEntry<BaseType>
: never;

export {};
36 changes: 34 additions & 2 deletions test-d/entries.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {expectAssignable} from 'tsd';
import type {Entries} from '../index.d.ts';
import {expectAssignable, expectType} from 'tsd';
import type {Entries, ObjectEntries, ObjectEntry} from '../index.d.ts';
import type {Entry} from '../source/entry.d.ts';

// Objects
Expand Down Expand Up @@ -49,3 +49,35 @@ const setEntries: Entries<typeof setExample> = [
setEntryNumber,
];
expectAssignable<Array<[(string | number), (string | number)]>>(setEntries);

// ObjectEntry
type Example = {
a: number;
b: string;
};

declare const exampleObjectEntry: ObjectEntry<Example>;
expectType<['a' | 'b', string | number]>(exampleObjectEntry);

// ObjectEntries
declare const exampleObjectEntries: ObjectEntries<Example>;
expectType<Array<['a' | 'b', string | number]>>(exampleObjectEntries);

// ObjectEntry and ObjectEntries preserve type information with generic type parameters
declare function getEntries<T extends Record<string, unknown>>(object: T): ObjectEntries<T>;
declare function getEntry<T extends Record<string, unknown>>(object: T): ObjectEntry<T>;

declare const genericEntries: ReturnType<typeof getEntries<Example>>;
expectType<Array<['a' | 'b', string | number]>>(genericEntries);

declare const genericEntry: ReturnType<typeof getEntry<Example>>;
expectType<['a' | 'b', string | number]>(genericEntry);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expectType<['a' | 'b', string | number]>(genericEntry);
expectType<['a' | 'b', string | number]>(genericEntry);
// Entry and Entries can widen in generic object contexts because they are conditional over collection kinds.
type EntryAssignability<BaseType extends Record<string, unknown>, EntryType extends [keyof BaseType, BaseType[keyof BaseType]]> = EntryType;
// @ts-expect-error -- Entry<BaseType> can widen to the generic constraint when BaseType is generic.
type EntryAssignabilityTest<BaseType extends Record<string, unknown>> = EntryAssignability<BaseType, Entry<BaseType>>;
type EntriesAssignability<BaseType extends Record<string, unknown>, EntriesType extends Array<[keyof BaseType, BaseType[keyof BaseType]]>> = EntriesType;
// @ts-expect-error -- Entries<BaseType> can widen to the generic constraint when BaseType is generic.
type EntriesAssignabilityTest<BaseType extends Record<string, unknown>> = EntriesAssignability<BaseType, Entries<BaseType>>;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the suggestion — thanks for pointing out this case. These tests clearly document why ObjectEntry/ObjectEntries are needed as direct alternatives.


// Entry and Entries can widen in generic object contexts because they are conditional over collection kinds.
type EntryAssignability<BaseType extends Record<string, unknown>, EntryType extends [keyof BaseType, BaseType[keyof BaseType]]> = EntryType;
// @ts-expect-error -- Entry<BaseType> can widen to the generic constraint when BaseType is generic.
type EntryAssignabilityTest<BaseType extends Record<string, unknown>> = EntryAssignability<BaseType, Entry<BaseType>>;

type EntriesAssignability<BaseType extends Record<string, unknown>, EntriesType extends Array<[keyof BaseType, BaseType[keyof BaseType]]>> = EntriesType;
// @ts-expect-error -- Entries<BaseType> can widen to the generic constraint when BaseType is generic.
type EntriesAssignabilityTest<BaseType extends Record<string, unknown>> = EntriesAssignability<BaseType, Entries<BaseType>>;