Skip to content

Commit

Permalink
fix: invert 명확한 타입 추론을 위한 타입 개선 (#219)
Browse files Browse the repository at this point in the history
* fix: invert 명확한 타입 추론을 위핸 타입 개선

* fix: invert 타입 개선
  • Loading branch information
ssi02014 authored Jun 13, 2024
1 parent b239f85 commit d8e2e68
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 15 deletions.
34 changes: 28 additions & 6 deletions docs/docs/utils/object/invert.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@

## Interface
```ts title="typescript"
const invert: (
obj: Record<PropertyKey, any>,
keyTransformer?: (value: any) => PropertyKey
) => Record<PropertyKey, any>;
const invert: <
K extends PropertyKey,
V,
TK extends PropertyKey = V extends PropertyKey ? V : PropertyKey
>(
obj: Record<K, V>,
keyTransformer?: (value: V) => TK
) => Record<TK, Exclude<K, symbol>>;
```

## Usage
Expand All @@ -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<number, "a" | "b" | "c">
```

### KeyTransformer
Expand All @@ -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<string, "a" | "b">
```

### 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">
```
23 changes: 16 additions & 7 deletions packages/utils/src/object/invert/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { identity } from '../../common';
const defaultKeyTransformer = <V, TK extends PropertyKey>(value: V) => {
return value as unknown as TK;
};

export const invert = (
obj: Record<PropertyKey, any>,
keyTransformer: (value: any) => PropertyKey = identity
export const invert = <
K extends PropertyKey,
V,
TK extends PropertyKey = V extends PropertyKey ? V : PropertyKey
>(
obj: Record<K, V>,
keyTransformer: (value: V) => TK = defaultKeyTransformer<V, TK>
) => {
const invertedObj: Record<PropertyKey, any> = {};
const invertedObj = {} as Record<TK, Exclude<K, symbol>>;
const keys = Object.keys(obj) as Exclude<K, symbol>[];

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;
Expand Down
37 changes: 35 additions & 2 deletions packages/utils/src/object/invert/invert.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,47 @@ describe('invert', () => {
const result = invert(obj);

expect(result).toEqual({ 1: 'a', 2: 'b', 3: 'c' });
expectTypeOf(result).toEqualTypeOf<Record<number, 'a' | 'b' | 'c'>>();
});

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<Record<1, 'a' | 'b' | 'c'>>();
});

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<Record<'foo' | 'bar', 'a' | 'b'>>();
});

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<Record<1 | 2, 'foo'>>();
});

it('should handle an empty object', () => {
Expand Down

0 comments on commit d8e2e68

Please sign in to comment.