Skip to content

Commit

Permalink
replaceKeys method (#213)
Browse files Browse the repository at this point in the history
* docs: Fix typo in JSDocs

* feat: Add a shallow replaceKeys method

* Regenerate package-lock

* test: Add some additional tests for non-literals

* docs: Fix mistake in JSDoc
  • Loading branch information
gustavoguichard authored Jun 19, 2024
1 parent 38f8875 commit 5a29efa
Show file tree
Hide file tree
Showing 16 changed files with 1,345 additions and 842 deletions.
2,008 changes: 1,205 additions & 803 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ export type { PascalKeys } from './utils/object-keys/pascal-keys.js'
export { pascalKeys } from './utils/object-keys/pascal-keys.js'
export type { SnakeKeys } from './utils/object-keys/snake-keys.js'
export { snakeKeys } from './utils/object-keys/snake-keys.js'
// Object keys transformation
export type { ReplaceKeys } from './utils/object-keys/replace-keys.js'
export { replaceKeys } from './utils/object-keys/replace-keys.js'

// Object keys word casing (deep)
export type { DeepCamelKeys } from './utils/object-keys/deep-camel-keys.js'
Expand Down
2 changes: 1 addition & 1 deletion src/utils/object-keys/camel-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { transformKeys } from './transform-keys.js'
import { type CamelCase, camelCase } from '../word-case/camel-case.js'

/**
* Shallowly transforms the keys of an Record to camelCase.
* Shallowly transforms the keys of a Record to camelCase.
* T: the type of the Record to transform.
*/
export type CamelKeys<T> = T extends []
Expand Down
2 changes: 1 addition & 1 deletion src/utils/object-keys/constant-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { transformKeys } from './transform-keys.js'
import { type ConstantCase, constantCase } from '../word-case/constant-case.js'

/**
* Shallowly transforms the keys of an Record to CONSTANT_CASE.
* Shallowly transforms the keys of a Record to CONSTANT_CASE.
* T: the type of the Record to transform.
*/
export type ConstantKeys<T> = T extends []
Expand Down
10 changes: 5 additions & 5 deletions src/utils/object-keys/deep-camel-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { type CamelCase, camelCase } from '../word-case/camel-case.js'
import { deepTransformKeys } from './deep-transform-keys.js'

/**
* Recursively transforms the keys of an Record to camelCase.
* Recursively transforms the keys of a Record to camelCase.
* T: the type of the Record to transform.
*/
export type DeepCamelKeys<T> = T extends [any, ...any]
? { [I in keyof T]: DeepCamelKeys<T[I]> }
: T extends (infer V)[]
? DeepCamelKeys<V>[]
: {
[K in keyof T as CamelCase<Extract<K, string>>]: DeepCamelKeys<T[K]>
}
? DeepCamelKeys<V>[]
: {
[K in keyof T as CamelCase<Extract<K, string>>]: DeepCamelKeys<T[K]>
}
/**
* A strongly typed function that recursively transforms the keys of an object to camelCase. The transformation is done both at runtime and type level.
* @param obj the object to transform.
Expand Down
12 changes: 7 additions & 5 deletions src/utils/object-keys/deep-constant-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,18 @@ import { type ConstantCase, constantCase } from '../word-case/constant-case.js'
import { deepTransformKeys } from './deep-transform-keys.js'

/**
* Recursively transforms the keys of an Record to CONSTANT_CASE.
* Recursively transforms the keys of a Record to CONSTANT_CASE.
* T: the type of the Record to transform.
*/
export type DeepConstantKeys<T> = T extends [any, ...any]
? { [I in keyof T]: DeepConstantKeys<T[I]> }
: T extends (infer V)[]
? DeepConstantKeys<V>[]
: {
[K in keyof T as ConstantCase<Extract<K, string>>]: DeepConstantKeys<T[K]>
}
? DeepConstantKeys<V>[]
: {
[K in keyof T as ConstantCase<Extract<K, string>>]: DeepConstantKeys<
T[K]
>
}
/**
* A strongly typed function that recursively transforms the keys of an object to CONSTANT_CASE. The transformation is done both at runtime and type level.
* @param obj the object to transform.
Expand Down
16 changes: 8 additions & 8 deletions src/utils/object-keys/deep-delimiter-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import {
import { deepTransformKeys } from './deep-transform-keys.js'

/**
* Recursively transforms the keys of an Record to a custom delimiter case.
* Recursively transforms the keys of a Record to a custom delimiter case.
* T: the type of the Record to transform.
* D: the delimiter to use.
*/
export type DeepDelimiterKeys<T, D extends string> = T extends [any, ...any]
? { [I in keyof T]: DeepDelimiterKeys<T[I], D> }
: T extends (infer V)[]
? DeepDelimiterKeys<V, D>[]
: {
[K in keyof T as DelimiterCase<Extract<K, string>, D>]: DeepDelimiterKeys<
T[K],
D
>
}
? DeepDelimiterKeys<V, D>[]
: {
[K in keyof T as DelimiterCase<
Extract<K, string>,
D
>]: DeepDelimiterKeys<T[K], D>
}
/**
* A strongly typed function that recursively transforms the keys of an object to a custom delimiter case. The transformation is done both at runtime and type level.
* @param obj the object to transform.
Expand Down
10 changes: 5 additions & 5 deletions src/utils/object-keys/deep-kebab-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { type KebabCase, kebabCase } from '../word-case/kebab-case.js'
import { deepTransformKeys } from './deep-transform-keys.js'

/**
* Recursively transforms the keys of an Record to kebab-case.
* Recursively transforms the keys of a Record to kebab-case.
* T: the type of the Record to transform.
*/
export type DeepKebabKeys<T> = T extends [any, ...any]
? { [I in keyof T]: DeepKebabKeys<T[I]> }
: T extends (infer V)[]
? DeepKebabKeys<V>[]
: {
[K in keyof T as KebabCase<Extract<K, string>>]: DeepKebabKeys<T[K]>
}
? DeepKebabKeys<V>[]
: {
[K in keyof T as KebabCase<Extract<K, string>>]: DeepKebabKeys<T[K]>
}
/**
* A strongly typed function that recursively transforms the keys of an object to kebab-case. The transformation is done both at runtime and type level.
* @param obj the object to transform.
Expand Down
10 changes: 5 additions & 5 deletions src/utils/object-keys/deep-pascal-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { type PascalCase, pascalCase } from '../word-case/pascal-case.js'
import { deepTransformKeys } from './deep-transform-keys.js'

/**
* Recursively transforms the keys of an Record to PascalCase.
* Recursively transforms the keys of a Record to PascalCase.
* T: the type of the Record to transform.
*/
export type DeepPascalKeys<T> = T extends [any, ...any]
? { [I in keyof T]: DeepPascalKeys<T[I]> }
: T extends (infer V)[]
? DeepPascalKeys<V>[]
: {
[K in keyof T as PascalCase<Extract<K, string>>]: DeepPascalKeys<T[K]>
}
? DeepPascalKeys<V>[]
: {
[K in keyof T as PascalCase<Extract<K, string>>]: DeepPascalKeys<T[K]>
}
/**
* A strongly typed function that recursively transforms the keys of an object to pascal case. The transformation is done both at runtime and type level.
* @param obj the object to transform.
Expand Down
10 changes: 5 additions & 5 deletions src/utils/object-keys/deep-snake-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import { type SnakeCase, snakeCase } from '../word-case/snake-case.js'
import { deepTransformKeys } from './deep-transform-keys.js'

/**
* Recursively transforms the keys of an Record to snake_case.
* Recursively transforms the keys of a Record to snake_case.
* T: the type of the Record to transform.
*/
export type DeepSnakeKeys<T> = T extends [any, ...any]
? { [I in keyof T]: DeepSnakeKeys<T[I]> }
: T extends (infer V)[]
? DeepSnakeKeys<V>[]
: {
[K in keyof T as SnakeCase<Extract<K, string>>]: DeepSnakeKeys<T[K]>
}
? DeepSnakeKeys<V>[]
: {
[K in keyof T as SnakeCase<Extract<K, string>>]: DeepSnakeKeys<T[K]>
}
/**
* A strongly typed function that recursively transforms the keys of an object to snake_case. The transformation is done both at runtime and type level.
* @param obj the object to transform.
Expand Down
2 changes: 1 addition & 1 deletion src/utils/object-keys/delimiter-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from '../word-case/delimiter-case.js'

/**
* Shallowly transforms the keys of an Record to a custom delimiter case.
* Shallowly transforms the keys of a Record to a custom delimiter case.
* T: the type of the Record to transform.
* D: the delimiter to use.
*/
Expand Down
2 changes: 1 addition & 1 deletion src/utils/object-keys/kebab-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type KebabCase, kebabCase } from '../word-case/kebab-case.js'
import { transformKeys } from './transform-keys.js'

/**
* Shallowly transforms the keys of an Record to kebab-case.
* Shallowly transforms the keys of a Record to kebab-case.
* T: the type of the Record to transform.
*/
export type KebabKeys<T> = T extends []
Expand Down
2 changes: 1 addition & 1 deletion src/utils/object-keys/pascal-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { type PascalCase, pascalCase } from '../word-case/pascal-case.js'
import { transformKeys } from './transform-keys.js'

/**
* Shallowly transforms the keys of an Record to PascalCase.
* Shallowly transforms the keys of a Record to PascalCase.
* T: the type of the Record to transform.
*/
export type PascalKeys<T> = T extends []
Expand Down
67 changes: 67 additions & 0 deletions src/utils/object-keys/replace-keys.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { type ReplaceKeys, replaceKeys } from './replace-keys.js'

namespace TypeTransforms {
type test = Expect<
Equal<
ReplaceKeys<
{
'some-value': { 'deep-nested': true }
'other-value': true
},
'some-',
''
>,
{ value: { 'deep-nested': true }; 'other-value': true }
>
>
type testWithUnion = Expect<
Equal<
ReplaceKeys<Record<'foo' | 'bar', string>, 'oo', 'izz'>,
Record<'fizz' | 'bar', string>
>
>
type test2 = Expect<
Equal<
ReplaceKeys<Record<'some nice string', string>, RegExp, '-'>,
Record<string, string>
>
>
type test3 = Expect<
Equal<ReplaceKeys<Record<string, string>, ' ', '-'>, Record<string, string>>
>
type test4 = Expect<
Equal<
ReplaceKeys<Record<Uppercase<string>, string>, ' ', '-'>,
Record<string, string>
>
>
type test5 = Expect<
Equal<
ReplaceKeys<Record<'some nice string', string>, string, '-'>,
Record<string, string>
>
>
type test6 = Expect<
Equal<
ReplaceKeys<Record<'some nice string', string>, ' ', string>,
Record<string, string>
>
>
}

test('replaceKeys', () => {
const expected = {
some: { deepNested: { value: true } },
value: true,
}
const result = replaceKeys(
{
some: { deepNested: { value: true } },
other_value: true,
},
'other_',
'',
)
expect(result).toEqual(expected)
type test = Expect<Equal<typeof result, typeof expected>>
})
29 changes: 29 additions & 0 deletions src/utils/object-keys/replace-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { transformKeys } from './transform-keys.js'
import { type Replace, replace } from '../../native/replace.js'

/**
* Shallowly transforms the keys of a Record with `replace`.
* T: the type of the Record to transform.
*/
export type ReplaceKeys<
T,
lookup extends string | RegExp,
replacement extends string = '',
> = T extends []
? T
: { [K in keyof T as Replace<Extract<K, string>, lookup, replacement>]: T[K] }
/**
* A strongly typed function that shallowly transforms the keys of an object by running the `replace` method in every key. The transformation is done both at runtime and type level.
* @param obj the object to transform.
* @param lookup the lookup string to be replaced.
* @param replacement the replacement string.
* @returns the transformed object.
* @example replaceKeys({ 'foo-bar': { 'fizz-buzz': true } }, 'f', 'b') // { booBar: { 'fizz-buz': true } }
*/
export function replaceKeys<
T,
S extends string | RegExp,
R extends string = '',
>(obj: T, lookup: S, replacement: R = '' as R): ReplaceKeys<T, S, R> {
return transformKeys(obj, (s) => replace(s, lookup, replacement)) as never
}
2 changes: 1 addition & 1 deletion src/utils/object-keys/snake-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { transformKeys } from './transform-keys.js'
import { type SnakeCase, snakeCase } from '../word-case/snake-case.js'

/**
* Shallowly transforms the keys of an Record to snake_case.
* Shallowly transforms the keys of a Record to snake_case.
* T: the type of the Record to transform.
*/
export type SnakeKeys<T> = T extends []
Expand Down

0 comments on commit 5a29efa

Please sign in to comment.