diff --git a/docs/docs/utils/object/invert.md b/docs/docs/utils/object/invert.md index fe5998552..249b0b522 100644 --- a/docs/docs/utils/object/invert.md +++ b/docs/docs/utils/object/invert.md @@ -11,10 +11,14 @@ ## Interface ```ts title="typescript" -const invert: ( - obj: Record, - keyTransformer?: (value: any) => PropertyKey -) => Record; +const invert: < + K extends PropertyKey, + V, + TK extends PropertyKey = V extends PropertyKey ? V : PropertyKey +>( + obj: Record, + keyTransformer?: (value: V) => TK +) => Record>; ``` ## Usage @@ -24,7 +28,9 @@ import { invert } from '@modern-kit/utils'; const obj = { a: 1, b: 2, c: 3 }; -invert(obj); // { 1: 'a', 2: 'b', 3: 'c' }; +invert(obj); +// value: { 1: 'a', 2: 'b', 3: 'c' }; +// type: Record ``` ### KeyTransformer @@ -35,5 +41,21 @@ const obj = { a: [1, 2, 3], b: [4, 5, 6] }; invert(obj, (value) => { return JSON.stringify(value); -}); // { '[1,2,3]': 'a', '[4,5,6]': 'b' } +}); +// value: { '[1,2,3]': 'a', '[4,5,6]': 'b' } +// type: Record +``` + +### const assertion +```ts title="typescript" +import { invert } from '@modern-kit/utils'; + +const obj = { + a: { name: 'foo' }, + b: { name: 'bar' }, +} as const; + +invert(obj, (value) => value.name); +// value: { foo: 'a', bar: 'b' } +// type: Record<"foo" | "bar", "a" | "b"> ``` \ No newline at end of file diff --git a/packages/utils/src/object/invert/index.ts b/packages/utils/src/object/invert/index.ts index ae17e35ad..3ee674c88 100644 --- a/packages/utils/src/object/invert/index.ts +++ b/packages/utils/src/object/invert/index.ts @@ -1,14 +1,23 @@ -import { identity } from '../../common'; +const defaultKeyTransformer = (value: V) => { + return value as unknown as TK; +}; -export const invert = ( - obj: Record, - keyTransformer: (value: any) => PropertyKey = identity +export const invert = < + K extends PropertyKey, + V, + TK extends PropertyKey = V extends PropertyKey ? V : PropertyKey +>( + obj: Record, + keyTransformer: (value: V) => TK = defaultKeyTransformer ) => { - const invertedObj: Record = {}; + const invertedObj = {} as Record>; + const keys = Object.keys(obj) as Exclude[]; - Object.keys(obj).forEach((key) => { + keys.forEach((key) => { const value = obj[key]; - invertedObj[keyTransformer(value)] = key; + const transformedKey = keyTransformer(value); + + invertedObj[transformedKey] = key; }); return invertedObj; diff --git a/packages/utils/src/object/invert/invert.spec.ts b/packages/utils/src/object/invert/invert.spec.ts index 40b4f6d6e..a451e481a 100644 --- a/packages/utils/src/object/invert/invert.spec.ts +++ b/packages/utils/src/object/invert/invert.spec.ts @@ -6,14 +6,47 @@ describe('invert', () => { const result = invert(obj); expect(result).toEqual({ 1: 'a', 2: 'b', 3: 'c' }); + expectTypeOf(result).toEqualTypeOf>(); }); - it('should use the keyTransformer if provided', () => { + it('should use the keyTransformer if provided(1)', () => { const obj = { a: 1, b: 2, c: 3 }; - const keyTransformer = (value: number) => `key_${value}`; + const keyTransformer = (value: number): `key_${number}` => `key_${value}`; const result = invert(obj, keyTransformer); expect(result).toEqual({ key_1: 'a', key_2: 'b', key_3: 'c' }); + expectTypeOf(result).toEqualTypeOf< + Record<`key_${number}`, 'a' | 'b' | 'c'> + >(); + }); + + it('should use the keyTransformer if provided(2)', () => { + const obj = { a: [1, 2, 3], b: [1, 2, 3], c: [1, 2, 3] } as const; + const result = invert(obj, (value) => value[0]); + + expect(result).toEqual({ 1: 'c' }); + expectTypeOf(result).toEqualTypeOf>(); + }); + + it('should use the keyTransformer if provided(3)', () => { + const obj = { + a: { name: 'foo' }, + b: { name: 'bar' }, + } as const; + const result = invert(obj, (value) => value.name); + + expect(result).toEqual({ foo: 'a', bar: 'b' }); + expectTypeOf(result).toEqualTypeOf>(); + }); + + it('should exclude keys that contain symbols.', () => { + const symbol = Symbol(1); + + const obj = { [symbol]: 1, foo: 2 } as const; + const result = invert(obj); + + expect(result).toEqual({ 2: 'foo' }); + expectTypeOf(result).toEqualTypeOf>(); }); it('should handle an empty object', () => {