Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Entry/Entries #115

Closed
dimitropoulos opened this issue Jun 13, 2020 · 12 comments · Fixed by #130
Closed

Entry/Entries #115

dimitropoulos opened this issue Jun 13, 2020 · 12 comments · Fixed by #130
Labels
help wanted Extra attention is needed type addition

Comments

@dimitropoulos
Copy link
Contributor

(this is a cross post of piotrwitek/utility-types#132 since it got no attention there)

I would like to gauge interest in this before making a PR.

Is your feature request related to a real problem or use-case?

  • I am using Object.entries.
  • I am using toPairs (ramda, lodash) in the context of a pipe (ramda, lodash), and want to access the type of the entries in terms of the original object.

Describe a solution including usage in code example

export type Entry<T> = [keyof T, T[keyof T]];
export type Entries<T> = Entry<T>[];

Who does this impact? Who is this for?

Anyone using object entries.

@sindresorhus
Copy link
Owner

Can't you use typeof on the array returned from Object.entries()?

@dimitropoulos
Copy link
Contributor Author

dimitropoulos commented Jun 14, 2020

No typeof doesn't quite settle it: consider a related case to the use case of the 2nd bullet above:

Let's say I have a function that has an argument whose type is Entry<T> or Entries<T>. In this case there's no Object.entries() call to make use of.

@sindresorhus
Copy link
Owner

Isn't Entry a bit too general? How about something like KeyValueEntry or ObjectEntry?

@dimitropoulos
Copy link
Contributor Author

dimitropoulos commented Jun 15, 2020

I don't think it's too general. To me, in the context of JavaScript, entry only can mean one thing. It's the technical term for the thing that returns from Object.entries. But sure, I'd be fine with ObjectEntry and ObjectEntries.

@sindresorhus
Copy link
Owner

There's also Map#entries(). Could this type be used on that?

@dimitropoulos
Copy link
Contributor Author

sure, if we wanted to support Maps, we would have to create a parallel implementation for Maps that we switch between:

// JUST Objects supported
export type ObjectEntry<T> = [keyof T, T[keyof T]];
export type ObjectEntries<T> = ObjectEntry<T>[];

const a = { a: 1 };

type AEntry = ObjectEntry<typeof a>;
type AEntries = ObjectEntries<typeof a>;

const aEntry: AEntry = ['a', 1];
const aEntries: AEntries = [aEntry];


// JUST Maps supported
export type MapKey<M> = M extends Map<infer T, {}> ? T : never;
export type MapValue<M> = M extends Map<{}, infer T> ? T : never;
export type MapEntry<T> = [MapKey<T>, MapValue<T>];
export type MapEntries<T> = MapEntry<T>[];

const b = new Map([['b', 2]]);

type BEntry = MapEntry<typeof b>;
type BEntries = MapEntries<typeof b>;

const yEntry: BEntry = ['b', 2];
const yEntries: BEntries = [yEntry];


// BOTH Objects and Maps supported
export type Entry<T> = T extends Map<any, any> ? MapEntry<T> : ObjectEntry<T>;
export type Entries<T> = T extends Map<any, any> ? MapEntries<T> : ObjectEntries<T>;

const c = { c: 3 };
const cEntry: Entry<typeof c> = ['c', 3];
const cEntries: Entries<typeof c> = [cEntry];

const d = new Map([['d', 4]]);
const dEntry: Entry<typeof d> = ['d', 4];
const dEntries: Entries<typeof d> = [dEntry]

@sindresorhus
Copy link
Owner

I think we should export only two types that supports Object, Array, Map, WeakMap, and Set. They can be split up into multiple types internally, but the exported should be something that can handle all these types (similar to what we've done in the past for other types: https://github.com/sindresorhus/type-fest/blob/master/source/partial-deep.d.ts).

@sindresorhus sindresorhus added help wanted Extra attention is needed type addition labels Aug 29, 2020
@dimitropoulos
Copy link
Contributor Author

dimitropoulos commented Aug 31, 2020

Ok so something like this?

Note that WeakMap and WeakSet are not enumerable and do not have .entries.

const objectExample = { a: 1 };
const mapExample = new Map([['a', 1]]);
const arrayExample = ['a', 1];
const setExample = new Set(['a', 1]);

// JUST Objects supported
/** https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries */
type ObjectEntry<T> = [keyof T, T[keyof T]];
type ObjectEntries<T> = ObjectEntry<T>[];
const objectEntry: ObjectEntry<typeof objectExample> = ['a', 1];
const objectEntries: ObjectEntries<typeof objectExample> = [objectEntry];

// JUST Maps supported
type MapKey<M> = M extends Map<infer T, unknown> ? T : never;
type MapValue<M> = M extends Map<unknown, infer T> ? T : never;

/** https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries */
type MapEntry<T> = [MapKey<T>, MapValue<T>];
type MapEntries<T> = MapEntry<T>[];
const mapEntry: MapEntry<typeof mapExample> = ['a', 1];
const mapEntries: MapEntries<typeof mapExample> = [mapEntry];

// JUST Arrays supported
/** https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries */
type ArrayEntry<T extends readonly unknown[]> = [number, T[number]];
type ArrayEntries<T extends readonly unknown[]> = ArrayEntry<T>[];
const arrayEntry: ArrayEntry<typeof arrayExample> = [0, 'a'];
const arrayEntries: ArrayEntries<typeof arrayExample> = [arrayEntry];

// Just Set supported
/** https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/entries */
type SetEntry<T> = T extends Set<infer U> ? [U, U] : never;
type SetEntries<T extends Set<unknown>> = SetEntry<T>[];
const setEntryString: SetEntry<typeof setExample> = ['a', 'a'];
const setEntryNumber: SetEntry<typeof setExample> = [1, 1];
const setEntries: SetEntries<typeof setExample> = [setEntryString, setEntryNumber];

// Note: `Weakmap`s and `WeakSet`s are not ennumerable and do not have and `entries` method

// BOTH Objects and Maps and Sets supported
export type Entry<T> =
    T extends Map<unknown, unknown> ? MapEntry<T>
  : T extends Set<unknown> ? SetEntry<T>
  : T extends Array<unknown> ? ArrayEntry<T>
  : T extends object ? ObjectEntry<T>
  : never;

export type Entries<T> =
    T extends Map<unknown, unknown> ? MapEntries<T>
  : T extends Set<unknown> ? SetEntries<T>
  : T extends Array<unknown> ? ArrayEntries<T>
  : T extends object ? ObjectEntries<T>
  : never;

const finalObjectEntry: Entry<typeof objectExample> = ['a', 1];
const finalObjectEntries: Entries<typeof objectExample> = [finalObjectEntry];

const finalMap = new Map([['a', 1]]);
const finalMapEntry: Entry<typeof finalMap> = ['a', 1];
const finalMapEntries: Entries<typeof finalMap> = [finalMapEntry];

const finalArrayEntryString: Entry<typeof arrayExample> = [0, 'a'];
const finalArrayEntryNumber: Entry<typeof arrayExample> = [1, 1];
const finalArrayEntries: Entries<typeof arrayExample> = [
  finalArrayEntryString,
  finalArrayEntryNumber,
];

const finalSetEntryString: Entry<typeof setExample> = ['a', 'a'];
const finalSetEntryNumber: Entry<typeof setExample> = [1, 1];
const finalSetEntries: Entries<typeof setExample> = [
  finalSetEntryString,
  finalSetEntryNumber,
];

@sindresorhus
Copy link
Owner

Yup. Object => object

@dimitropoulos
Copy link
Contributor Author

ahh yes, good catch, I updated Entry and Entries to use extends object instead of extends Object

@dimitropoulos
Copy link
Contributor Author

shall I make a PR?

@sindresorhus
Copy link
Owner

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed type addition
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants