diff --git a/index.d.ts b/index.d.ts index 8a9926d8d..ee8a973d4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -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'; diff --git a/readme.md b/readme.md index 47e9fee30..54a207953 100644 --- a/readme.md +++ b/readme.md @@ -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. diff --git a/source/entries.d.ts b/source/entries.d.ts index 204945514..d0500cbc0 100644 --- a/source/entries.d.ts +++ b/source/entries.d.ts @@ -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 = Array<_ArrayEntry>; type MapEntries = Array<_MapEntry>; -type ObjectEntries = Array<_ObjectEntry>; type SetEntries> = Array<_SetEntry>; +/** +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; +//=> ObjectEntry[] +``` + +@category Object +*/ +export type ObjectEntries = Array>; + /** 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. diff --git a/source/entry.d.ts b/source/entry.d.ts index 49cad1c12..f83f3e28d 100644 --- a/source/entry.d.ts +++ b/source/entry.d.ts @@ -3,7 +3,30 @@ type MapValue = BaseType extends Map ? Value export type _ArrayEntry = [number, BaseType[number]]; export type _MapEntry = [MapKey, MapValue]; -export type _ObjectEntry = [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; +//=> [keyof Example, number | string] +``` + +@category Object +*/ +export type ObjectEntry = [keyof BaseType, BaseType[keyof BaseType]]; export type _SetEntry = BaseType extends Set ? [ItemType, ItemType] : never; /** @@ -61,7 +84,7 @@ export type Entry = BaseType extends Map ? _MapEntry : BaseType extends Set ? _SetEntry : BaseType extends readonly unknown[] ? _ArrayEntry - : BaseType extends object ? _ObjectEntry + : BaseType extends object ? ObjectEntry : never; export {}; diff --git a/test-d/entries.ts b/test-d/entries.ts index 62a124343..ca1a94447 100644 --- a/test-d/entries.ts +++ b/test-d/entries.ts @@ -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 @@ -49,3 +49,35 @@ const setEntries: Entries = [ setEntryNumber, ]; expectAssignable>(setEntries); + +// ObjectEntry +type Example = { + a: number; + b: string; +}; + +declare const exampleObjectEntry: ObjectEntry; +expectType<['a' | 'b', string | number]>(exampleObjectEntry); + +// ObjectEntries +declare const exampleObjectEntries: ObjectEntries; +expectType>(exampleObjectEntries); + +// ObjectEntry and ObjectEntries preserve type information with generic type parameters +declare function getEntries>(object: T): ObjectEntries; +declare function getEntry>(object: T): ObjectEntry; + +declare const genericEntries: ReturnType>; +expectType>(genericEntries); + +declare const genericEntry: ReturnType>; +expectType<['a' | 'b', string | number]>(genericEntry); + +// Entry and Entries can widen in generic object contexts because they are conditional over collection kinds. +type EntryAssignability, EntryType extends [keyof BaseType, BaseType[keyof BaseType]]> = EntryType; +// @ts-expect-error -- Entry can widen to the generic constraint when BaseType is generic. +type EntryAssignabilityTest> = EntryAssignability>; + +type EntriesAssignability, EntriesType extends Array<[keyof BaseType, BaseType[keyof BaseType]]>> = EntriesType; +// @ts-expect-error -- Entries can widen to the generic constraint when BaseType is generic. +type EntriesAssignabilityTest> = EntriesAssignability>;