remeda
Version:
A utility library for JavaScript and Typescript.
1 lines • 7.05 kB
Source Map (JSON)
{"version":3,"file":"omit.cjs","names":["purry","hasAtLeast"],"sources":["../src/omit.ts"],"sourcesContent":["import type { EmptyObject, IsNever, KeysOfUnion } from \"type-fest\";\nimport { hasAtLeast } from \"./hasAtLeast\";\nimport type { IsBounded } from \"./internal/types/IsBounded\";\nimport type { IsBoundedRecord } from \"./internal/types/IsBoundedRecord\";\nimport type { PartitionByUnion } from \"./internal/types/PartitionByUnion\";\nimport type { SimplifiedWritable } from \"./internal/types/SimplifiedWritable\";\nimport type { TupleParts } from \"./internal/types/TupleParts\";\nimport { purry } from \"./purry\";\n\ntype OmitFromArray<T, Keys extends readonly PropertyKey[]> =\n // Distribute unions for both object types and key arrays.\n T extends unknown\n ? Keys extends unknown\n ? // The output is always writable because we always create a new object!\n SimplifiedWritable<\n IsNever<Extract<Keys[number], keyof T>> extends true\n ? // When none of the keys belong to T we can short-circuit and\n // simply return T as-is because `omit` would do nothing.\n T\n : IsBoundedRecord<T> extends true\n ? OmitBounded<T, Keys>\n : OmitUnbounded<T, Keys>\n >\n : never\n : never;\n\ntype OmitBounded<T, Keys extends readonly PropertyKey[]> =\n // We build our output by first considering any key present in the keys array\n // as being omitted. This object would contain all keys that are unaffected at\n // all by this omit operation.\n FixEmpty<Omit<T, Keys[number]>> &\n // But we might be missing keys that are optional in the keys tuple (and\n // thus might not be removed). Because these keys are optional, their props\n // in the output also need to be optional.\n Partial<\n Pick<\n T,\n Exclude<\n // Find all keys that can either be omitted or not, these are all keys\n // in unions in the required parts of the keys tuple (the prefix and\n // the suffix), as well as all keys in the optional parts and the rest\n // item.\n | PartitionByUnion<TupleParts<Keys>[\"required\"]>[\"union\"]\n | TupleParts<Keys>[\"optional\"][number]\n | TupleParts<Keys>[\"item\"]\n | PartitionByUnion<TupleParts<Keys>[\"suffix\"]>[\"union\"],\n // We then need to remove from these any items which *also* are\n // ensured to always exist in the keys tuple, these are the elements\n // of the required parts of the tuple which are singular (not unions).\n | PartitionByUnion<TupleParts<Keys>[\"required\"]>[\"singular\"]\n | PartitionByUnion<TupleParts<Keys>[\"suffix\"]>[\"singular\"]\n >\n >\n >;\n\n/**\n * The built-in `Omit` type doesn't handle unbounded records correctly! When\n * omitting an unbounded key the result should be untouched as we can't tell\n * what got removed, and can't represent an object that had \"something\" removed\n * from it, but instead it returns `{}`(?!) The same thing applies when a key\n * is only optionally omitted for the same reasons. This is why we don't use\n * `Omit` at all for the unbounded case.\n *\n * @see https://www.typescriptlang.org/play/?#code/C4TwDgpgBAqgdgIwPYFc4BMLqgXigeQFsBLYAHgCUIBjJAJ3TIGdg7i4BzAGigCIALCABshSXgD4eLNp3EBuAFAB6JVDUA9APxA\n */\ntype OmitUnbounded<T, Keys extends readonly PropertyKey[]> = T &\n // Any key we know for sure is being omitted needs to become \"impossible\" to\n // access; for an unbounded record this means merging it with a bounded record\n // with `never` value for these keys.\n Record<\n Bounded<\n | PartitionByUnion<TupleParts<Keys>[\"required\"]>[\"singular\"]\n | PartitionByUnion<TupleParts<Keys>[\"suffix\"]>[\"singular\"]\n >,\n never\n >;\n\n/**\n * When `Omit` omits **all** keys from a bounded record it results in `{}` which\n * doesn't match what we'd expect to be returned in terms of a useful type as\n * the output of `Omit`.\n */\ntype FixEmpty<T> = IsNever<keyof T> extends true ? EmptyObject : T;\n\n/**\n * Filter a union of types, leaving only those that are bounded. e.g.,\n * `Bounded<\"a\" | number>` results in `\"a\"`.\n */\ntype Bounded<T> = T extends unknown\n ? IsBounded<T> extends true\n ? T\n : never\n : never;\n\n/**\n * Returns a partial copy of an object omitting the keys specified.\n *\n * @param keys - The property names.\n * @signature\n * R.omit(keys)(obj);\n * @example\n * R.pipe({ a: 1, b: 2, c: 3, d: 4 }, R.omit(['a', 'd'])) // => { b: 2, c: 3 }\n * @dataLast\n * @category Object\n */\nexport function omit<T, const Keys extends readonly KeysOfUnion<T>[]>(\n keys: Keys,\n): (data: T) => OmitFromArray<T, Keys>;\n\n/**\n * Returns a partial copy of an object omitting the keys specified.\n *\n * @param data - The object.\n * @param keys - The property names.\n * @signature\n * R.omit(obj, keys);\n * @example\n * R.omit({ a: 1, b: 2, c: 3, d: 4 }, ['a', 'd']) // => { b: 2, c: 3 }\n * @dataFirst\n * @category Object\n */\nexport function omit<T, const Keys extends readonly KeysOfUnion<T>[]>(\n data: T,\n keys: Keys,\n): OmitFromArray<T, Keys>;\n\nexport function omit(...args: readonly unknown[]): unknown {\n return purry(omitImplementation, args);\n}\n\nfunction omitImplementation<\n T extends object,\n Keys extends readonly (keyof T)[],\n>(data: T, keys: Keys): OmitFromArray<T, Keys> {\n if (!hasAtLeast(keys, 1)) {\n // No props to omit at all!\n // @ts-expect-error [ts2322] - TypeScript can't connect the fact that the\n // keys array is empty and infer the expected output, and then infer that we\n // return it correctly here.\n return { ...data };\n }\n\n if (!hasAtLeast(keys, 2)) {\n // Only one prop to omit so we can let the runtime engine deal with\n // removing it efficiently.\n const { [keys[0]]: _omitted, ...remaining } = data;\n\n // @ts-expect-error [ts2322] - TypeScript can't compute the expected output\n // correctly and then infer that we return it correctly here.\n return remaining;\n }\n\n // Multiple props to omit so we have to use a loop to omit all of them.\n const out = { ...data };\n for (const key of keys) {\n // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- This is intentional! It is the most effective way to allow the runtime engine to optimize the object without creating excessive copies for every omitted key.\n delete out[key];\n }\n\n // @ts-expect-error [ts2322] - The type is too complex and TypeScript can't\n // \"follow\" the iterative algorithm to ensure the output makes sense.\n return out;\n}\n"],"mappings":"+EA8HA,SAAgB,EAAK,GAAG,EAAmC,CACzD,OAAOA,EAAAA,EAAM,EAAoB,EAAK,CAGxC,SAAS,EAGP,EAAS,EAAoC,CAC7C,GAAI,CAACC,EAAAA,EAAW,EAAM,EAAE,CAKtB,MAAO,CAAE,GAAG,EAAM,CAGpB,GAAI,CAACA,EAAAA,EAAW,EAAM,EAAE,CAAE,CAGxB,GAAM,EAAG,EAAK,IAAK,EAAU,GAAG,GAAc,EAI9C,OAAO,EAIT,IAAM,EAAM,CAAE,GAAG,EAAM,CACvB,IAAK,IAAM,KAAO,EAEhB,OAAO,EAAI,GAKb,OAAO"}