diff --git a/projects/micro-dash/src/lib/interfaces.ts b/projects/micro-dash/src/lib/interfaces.ts index b84d87a2..1581abe8 100644 --- a/projects/micro-dash/src/lib/interfaces.ts +++ b/projects/micro-dash/src/lib/interfaces.ts @@ -2,6 +2,7 @@ export type Nil = null | undefined; export type Primitive = boolean | number | string; export type Key = keyof any; // TODO: replace with built-in PropertyKey export type Existent = Primitive | object; +export type EmptyObject = Record; export type ObjectWith = Record; export type StringifiedKey = Cast; diff --git a/projects/micro-dash/src/lib/object/omit.spec.ts b/projects/micro-dash/src/lib/object/omit.spec.ts index 62239e4e..45dd8b0d 100644 --- a/projects/micro-dash/src/lib/object/omit.spec.ts +++ b/projects/micro-dash/src/lib/object/omit.spec.ts @@ -1,6 +1,75 @@ +import { staticTest } from '@s-libs/ng-dev'; +import { expectTypeOf } from 'expect-type'; +import { EmptyObject } from '../interfaces'; import { omit } from './'; describe('omit()', () => { + it('has fancy typing', () => { + staticTest(() => { + const str = '' as string; + const num = 0 as number; + + interface Obj { + a: number; + b: Date; + } + const obj = {} as Obj; + const objOrN = {} as Obj | null; + const objOrU = {} as Obj | undefined; + expectTypeOf(omit(obj, 'b')).toEqualTypeOf<{ a: number }>(); + expectTypeOf(omit(objOrN, 'b')).toEqualTypeOf< + { a: number } | EmptyObject + >(); + expectTypeOf(omit(objOrU, 'b')).toEqualTypeOf< + { a: number } | EmptyObject + >(); + + const indexed = {} as { [k: string]: number; [k: number]: number }; + expectTypeOf(omit(indexed, 'hi')).toEqualTypeOf<{ + [x: string]: number; + [x: number]: number; + }>(); + expectTypeOf(omit(indexed, 5)).toEqualTypeOf<{ + [x: string]: number; + [x: number]: number; + }>(); + expectTypeOf(omit(indexed, 'hi', 5)).toEqualTypeOf<{ + [x: string]: number; + [x: number]: number; + }>(); + expectTypeOf(omit(indexed, str)).toEqualTypeOf<{ + [x: string]: number; + [x: number]: number; + }>(); + + const record = {} as Record; + expectTypeOf(omit(record, str)).toEqualTypeOf>(); + + const composite = {} as { [k: number]: Date; a: 'eh'; b: 'bee' }; + expectTypeOf(omit(composite, 'a')).toEqualTypeOf<{ + [x: number]: Date; + b: 'bee'; + }>(); + expectTypeOf(omit(composite, 'a', 'b')).toEqualTypeOf< + Record + >(); + expectTypeOf(omit(composite, 1)).toEqualTypeOf<{ + [x: number]: Date; + a: 'eh'; + b: 'bee'; + }>(); + expectTypeOf(omit(composite, num)).toEqualTypeOf<{ + [x: number]: Date; + a: 'eh'; + b: 'bee'; + }>(); + expectTypeOf(omit(composite, num, 'a')).toEqualTypeOf<{ + [x: number]: Date; + b: 'bee'; + }>(); + }); + }); + // // stolen from https://github.com/healthiers/mini-dash // diff --git a/projects/micro-dash/src/lib/object/omit.ts b/projects/micro-dash/src/lib/object/omit.ts index 6fd0d615..39953310 100644 --- a/projects/micro-dash/src/lib/object/omit.ts +++ b/projects/micro-dash/src/lib/object/omit.ts @@ -1,4 +1,4 @@ -import { Nil } from '../interfaces'; +import { EmptyObject, IfCouldBe, Nil } from '../interfaces'; import { clone } from '../lang'; type RemainingKeys = @@ -16,15 +16,21 @@ type RemainingKeys = * - Lodash: 16,006 bytes * - Micro-dash: 170 bytes */ -export function omit>( +export function omit< + T extends object | Nil, + O extends ReadonlyArray>, +>( object: T, ...paths: O -): { - [K in RemainingKeys]: T[K]; -} { +): + | { + [K in RemainingKeys, O[number]>]: Exclude[K]; + } + | IfCouldBe { + // TODO: test size of `??` const obj: any = clone(object) || {}; for (const path of paths) { - // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- well, this is exactly what the user requested, so ... + // eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- this is exactly what the user requested, so ... delete obj[path]; } return obj; diff --git a/projects/micro-dash/src/typing-tests/object/omit.dts-spec.ts b/projects/micro-dash/src/typing-tests/object/omit.dts-spec.ts deleted file mode 100644 index 9ce601f7..00000000 --- a/projects/micro-dash/src/typing-tests/object/omit.dts-spec.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { omit } from '../../lib/object'; - -declare const str: string; -declare const num: number; - -declare const obj: { - a: number; - b: Date; -}; -// $ExpectType { a: number; } -omit(obj, 'b'); - -declare const indexed: { - [k: string]: number; - [k: number]: number; -}; -// $ExpectType { [x: string]: number; [x: number]: number; } -omit(indexed, 'hi'); -// $ExpectType { [x: string]: number; [x: number]: number; } -omit(indexed, 5); -// $ExpectType { [x: string]: number; [x: number]: number; } -omit(indexed, 'hi', 5); -// $ExpectType { [x: string]: number; [x: number]: number; } -omit(indexed, str); - -declare const record: Record; -// $ExpectType { [x: string]: number; } -omit(record, str); - -declare const composite: { - [k: number]: Date; - a: 'eh'; - b: 'bee'; -}; -// $ExpectType { [x: number]: Date; b: "bee"; } -omit(composite, 'a'); -// $ExpectType { [x: number]: Date; } -omit(composite, 'a', 'b'); -// $ExpectType { [x: number]: Date; a: "eh"; b: "bee"; } -omit(composite, 1); -// $ExpectType { [x: number]: Date; a: "eh"; b: "bee"; } -omit(composite, num); -// $ExpectType { [x: number]: Date; b: "bee"; } -omit(composite, num, 'a');