remeda
Version:
A utility library for JavaScript and Typescript.
1 lines • 6.7 kB
Source Map (JSON)
{"version":3,"file":"fromEntries.cjs","names":["purry"],"sources":["../src/fromEntries.ts"],"sourcesContent":["import type { Simplify } from \"type-fest\";\nimport type { IterableContainer } from \"./internal/types/IterableContainer\";\nimport type { RemedaTypeError } from \"./internal/types/RemedaTypeError\";\nimport { purry } from \"./purry\";\n\ntype FromEntriesError<Message extends string> = RemedaTypeError<\n \"fromEntries\",\n Message\n>;\n\ntype Entry<Key extends PropertyKey = PropertyKey, Value = unknown> = readonly [\n key: Key,\n value: Value,\n];\n\n// The 2 kinds of arrays we accept result in different kinds of outputs:\n// 1. If the input is a *tuple*, we know exactly what entries it would hold,\n// and thus can type the result so that the keys are required. We will then run\n// recursively on the rest of the tuple.\n// 2. If the input is an *array* then any keys defined in the array might not\n// actually show up in runtime, and thus need to be optional. (e.g. if the input\n// is an empty array).\ntype FromEntries<Entries> = Entries extends readonly [\n infer First,\n ...infer Tail,\n]\n ? FromEntriesTuple<First, Tail>\n : Entries extends readonly [...infer Head, infer Last]\n ? FromEntriesTuple<Last, Head>\n : Entries extends IterableContainer<Entry>\n ? FromEntriesArray<Entries>\n : FromEntriesError<\"Entries array-like could not be inferred\">;\n\n// For strict tuples we build the result by intersecting each entry as a record\n// between it's key and value, recursively. The recursion goes through our main\n// type so that we support tuples which also contain rest parts.\ntype FromEntriesTuple<E, Rest> = E extends Entry\n ? FromEntries<Rest> & Record<E[0], E[1]>\n : FromEntriesError<\"Array-like contains a non-entry element\">;\n\n// For the array case we also need to handle what kind of keys it defines:\n// 1. If it defines a *broad* key (one that has an infinite set of values, like\n// number or string) then the result is a simple record.\n// 2. If the keys are *literals* then we need to make the record partial\n// (because those props are explicit), and we need to match each key it's\n// specific possible value, as defined by the entries.\n//\n// Note that this destination between keys is the result of how typescript\n// considers Record<string, unknown> to be **implicitly** partial, whereas\n// Record<\"a\", unknown> is not.\ntype FromEntriesArray<Entries extends IterableContainer<Entry>> =\n string extends AllKeys<Entries>\n ? Record<string, Entries[number][1]>\n : number extends AllKeys<Entries>\n ? Record<number, Entries[number][1]>\n : symbol extends AllKeys<Entries>\n ? Record<symbol, Entries[number][1]>\n : FromEntriesArrayWithLiteralKeys<Entries>;\n\n// This type is largely copied from `objectFromEntries` in the repo:\n// *sindresorhus/ts-extras* but makes all properties of the output optional,\n// which is more correct because we can't assure that an entry will exist for\n// every possible prop/key of the input.\n// @see https://github.com/sindresorhus/ts-extras/blob/44f57392c5f027268330771996c4fdf9260b22d6/source/object-from-entries.ts)\ntype FromEntriesArrayWithLiteralKeys<Entries extends IterableContainer<Entry>> =\n {\n [P in AllKeys<Entries>]?: ValueForKey<Entries, P>;\n };\n\ntype AllKeys<Entries extends IterableContainer<Entry>> = Extract<\n Entries[number],\n Entry\n>[0];\n\n// I tried and failed to simplify the type here! What the ternary does here is\n// to support the cases where the entries are defined by a single type that\n// defines all entries, but it defines the keys as a union of literals\n// (`['a' | 'b', number]`); which is different from the output of toPairs\n// which would define a separate tuple literal for each key (`['a', number] |\n// ['b', number]`). We need to support both cases!\ntype ValueForKey<\n Entries extends IterableContainer<Entry>,\n K extends PropertyKey,\n> = (Extract<Entries[number], Entry<K>> extends never\n ? Entries[number]\n : Extract<Entries[number], Entry<K>>)[1];\n\n/**\n * Creates a new object from an array of tuples by pairing up first and second elements as {[key]: value}.\n * If a tuple is not supplied for any element in the array, the element will be ignored\n * If duplicate keys exist, the tuple with the greatest index in the input array will be preferred.\n *\n * The strict option supports more sophisticated use-cases like those that would\n * result when calling the strict `toPairs` function.\n *\n * There are several other functions that could be used to build an object from\n * an array:\n * * `fromKeys` - Builds an object from an array of *keys* and a mapper for values.\n * * `indexBy` - Builds an object from an array of *values* and a mapper for keys.\n * * `pullObject` - Builds an object from an array of items with mappers for *both* keys and values.\n * Refer to the docs for more details.\n *\n * @param entries - An array of key-value pairs.\n * @signature\n * R.fromEntries(tuples)\n * @example\n * R.fromEntries([['a', 'b'], ['c', 'd']]); // => {a: 'b', c: 'd'}\n * @dataFirst\n * @category Object\n */\nexport function fromEntries<Entries extends IterableContainer<Entry>>(\n entries: Entries,\n): Simplify<FromEntries<Entries>>;\n\n/**\n * Creates a new object from an array of tuples by pairing up first and second elements as {[key]: value}.\n * If a tuple is not supplied for any element in the array, the element will be ignored\n * If duplicate keys exist, the tuple with the greatest index in the input array will be preferred.\n *\n * The strict option supports more sophisticated use-cases like those that would\n * result when calling the strict `toPairs` function.\n *\n * There are several other functions that could be used to build an object from\n * an array:\n * * `fromKeys` - Builds an object from an array of *keys* and a mapper for values.\n * * `indexBy` - Builds an object from an array of *values* and a mapper for keys.\n * * `pullObject` - Builds an object from an array of items with mappers for *both* keys and values.\n * Refer to the docs for more details.\n *\n * @signature\n * R.fromEntries()(tuples)\n * @example\n * R.pipe(\n * [['a', 'b'], ['c', 'd']] as const,\n * R.fromEntries(),\n * ); // => {a: 'b', c: 'd'}\n * @dataLast\n * @category Object\n */\nexport function fromEntries(): <Entries extends IterableContainer<Entry>>(\n entries: Entries,\n) => Simplify<FromEntries<Entries>>;\n\nexport function fromEntries(...args: readonly unknown[]): unknown {\n return purry(Object.fromEntries, args);\n}\n"],"mappings":"wCA+IA,SAAgB,EAAY,GAAG,EAAmC,CAChE,OAAOA,EAAAA,EAAM,OAAO,YAAa,EAAK"}