Skip to content

Commit

Permalink
Merge pull request #5 from kakasoo/feature/deep-strict-object-keys
Browse files Browse the repository at this point in the history
Function that returns an array with a key as an element deduced as `DeepStrictObjectKeys`
  • Loading branch information
kakasoo authored Jan 26, 2025
2 parents afea8c2 + 45ce3a8 commit 0166b92
Show file tree
Hide file tree
Showing 25 changed files with 1,146 additions and 276 deletions.
150 changes: 104 additions & 46 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"prettier": "^3.4.2",
"rimraf": "^6.0.1",
"ts-patch": "^3.3.0",
"typescript": "^5.7.3",
"typescript": "~5.7.3",
"typia": "^7.6.0"
},
"repository": {
Expand All @@ -62,4 +62,4 @@
"dependencies": {
"@kakasoo/proto-typescript": "^1.28.7"
}
}
}
4 changes: 2 additions & 2 deletions src/DeepStrictAssert.ts → src/functions/DeepStrictAssert.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DeepStrictObjectKeys } from './types/DeepStrictObjectKeys';
import { DeepStrictPick } from './types/DeepStrictPick';
import { DeepStrictObjectKeys } from '../types/DeepStrictObjectKeys';
import { DeepStrictPick } from '../types/DeepStrictPick';

export const deepStrictAssert =
<T extends object>(input: T) =>
Expand Down
24 changes: 24 additions & 0 deletions src/functions/DeepStrictObjectKeys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { DeepStrictObjectKeys } from '../types';

export function deepStrictObjectKeys<
T extends object,
Joiner extends { array: string; object: string } = { array: '[*]'; object: '.' },
>(target: T, joiner?: Joiner): DeepStrictObjectKeys<T, Joiner, false>[] {
if (joiner === undefined) {
joiner = { array: '[*]', object: '.' } as Joiner;
}

return Array.from(
new Set(
Object.entries(target).flatMap(([key, value]) => {
if (target instanceof Array) {
return deepStrictObjectKeys(value, joiner).map((el) => `${joiner.array}.${el}`);
} else if (target !== null && typeof target === 'object') {
return deepStrictObjectKeys(value, joiner).flatMap((el) => `${joiner.object}.${el}`);
} else {
return [];
}
}),
),
) as DeepStrictObjectKeys<T, Joiner>[];
}
2 changes: 2 additions & 0 deletions src/functions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './DeepStrictAssert';
export * from './DeepStrictObjectKeys';
21 changes: 2 additions & 19 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,2 @@
export * from './DeepStrictAssert';
export * from './types/DeepDateToString';
export * from './types/DeepStrictMerge';
export * from './types/DeepStrictObjectKeys';
export * from './types/DeepStrictObjectLastKeys';
export * from './types/DeepStrictOmit';
export * from './types/DeepStrictPick';
export * from './types/DeepStrictUnbrand';
export * from './types/ElementOf';
export * from './types/Equal';
export * from './types/GetMember';
export * from './types/GetType';
export * from './types/IsAny';
export * from './types/IsUnion';
export * from './types/RemoveAfterDot';
export * from './types/RemoveArraySymbol';
export * from './types/RemoveLastProperty';
export * from './types/StringToDeepObject';
export * from './types/ValueType';
export * from './functions';
export * from './types';
65 changes: 40 additions & 25 deletions src/types/DeepStrictObjectKeys.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
import { DeepStrictUnbrand } from './DeepStrictUnbrand';
import { Equal } from './Equal';
import type { IsAny } from './IsAny';
import type { IsUnion } from './IsUnion';
import type { ValueType } from './ValueType';

namespace DeepStrictObjectKeys {
export type Infer<
T extends object,
Joiner extends {
array: string;
object: string;
} = {
array: '[*]';
object: '.';
},
P extends keyof T = keyof T,
Target extends object,
Joiner extends { array: string; object: string } = { array: '[*]'; object: '.' },
IsSafe extends boolean = true,
P extends keyof Target = keyof Target,
> = P extends string
? IsUnion<T[P]> extends true
? P
: T[P] extends Array<infer Element extends object>
? P | `${P}${Joiner['array']}${Joiner['object']}${Infer<Element, Joiner>}`
: T[P] extends ValueType
? IsUnion<Target[P]> extends true
? Equal<IsSafe, true> extends true
? P
: // If a user wants to explore a type that is a union of primitive types and object types.
| P
| (Target[P] extends infer E
? E extends ValueType
? P
: E extends object
? E extends Array<infer _Element extends object>
? `${P}${Joiner['array']}${Joiner['object']}${Infer<_Element, Joiner, IsSafe>}` // recursive
: `${P}${Joiner['object']}${Infer<E, Joiner, IsSafe>}` // recursive
: never // Remove all primitive types of union types.
: never)
: Target[P] extends Array<infer Element extends object>
? P | `${P}${Joiner['array']}${Joiner['object']}${Infer<Element, Joiner, false>}`
: Target[P] extends ValueType
? P
: IsAny<T[P]> extends true
: IsAny<Target[P]> extends true
? P
: T[P] extends object
? T[P] extends Array<infer _Element>
: Target[P] extends object
? Target[P] extends Array<infer _Element>
? P
: T[P] extends Record<string, never>
: Target[P] extends Record<string, never>
? `${P}`
: `${P}` | `${P}${Joiner['object']}${Infer<T[P], Joiner>}`
: `${P}` | `${P}${Joiner['object']}${Infer<Target[P], Joiner, false>}`
: never
: never;
}
Expand All @@ -44,17 +52,24 @@ namespace DeepStrictObjectKeys {
* type Example1 = DeepStrictObjectKeys<{ a: { b: 1; c: 2 } }>; // "a" | "a.b" | "a.c"
* type Example2 = DeepStrictObjectKeys<{ a: { b: 1; c: { d: number }[] } }>; // "a" | "a.b" | "a.c" | "a.c[*].d"
* ```
* @template Target Destination type for which you want to pull a key
* @template Joiner It means what symbol to connect when recursively spinning superimposed types.
* @template IsSafe When a key is a combination type of a primitive type and an object, it means whether to perform a recursive search or not.
*/
export type DeepStrictObjectKeys<
T extends object,
Target extends object,
Joiner extends { array: string; object: string } = {
array: '[*]';
object: '.';
},
P extends keyof T = keyof T,
IsSafe extends boolean = true,
> =
DeepStrictUnbrand<T> extends Array<infer Element>
DeepStrictUnbrand<Target> extends Array<infer Element>
? Element extends object
? `${Joiner['array']}.${DeepStrictObjectKeys<Element, Joiner>}`
: `${Joiner['array']}.${keyof Element extends string ? keyof Element : never}`
: DeepStrictObjectKeys.Infer<DeepStrictUnbrand<T>, Joiner, Extract<P, keyof DeepStrictUnbrand<T>>>;
? `${Joiner['array']}.${DeepStrictObjectKeys<Element, Joiner, IsSafe>}`
: `${Joiner['array']}`
: DeepStrictUnbrand<Target> extends readonly (infer Element)[] // TODO: support tuple types
? Element extends object
? `${Joiner['array']}.${DeepStrictObjectKeys<Element, Joiner, IsSafe>}`
: `${Joiner['array']}`
: DeepStrictObjectKeys.Infer<DeepStrictUnbrand<Target>, Joiner, IsSafe>;
5 changes: 3 additions & 2 deletions src/types/DeepStrictUnbrand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ namespace DeepStrictUnbrand {
* type Example3 = DeepStrictUnbrand<Array<string & { __brand: 'email' }>>; // Array<string>
* ```
*/
export type DeepStrictUnbrand<T> =
T extends Array<Date>
export type DeepStrictUnbrand<T> = T extends []
? []
: T extends Array<Date>
? Array<Date>
: T extends Array<infer I extends object>
? Array<DeepStrictUnbrand<I>>
Expand Down
18 changes: 18 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export * from './DeepDateToString';
export * from './DeepStrictMerge';
export * from './DeepStrictObjectKeys';
export * from './DeepStrictObjectLastKeys';
export * from './DeepStrictOmit';
export * from './DeepStrictPick';
export * from './DeepStrictUnbrand';
export * from './ElementOf';
export * from './Equal';
export * from './GetMember';
export * from './GetType';
export * from './IsAny';
export * from './IsUnion';
export * from './RemoveAfterDot';
export * from './RemoveArraySymbol';
export * from './RemoveLastProperty';
export * from './StringToDeepObject';
export * from './ValueType';
168 changes: 0 additions & 168 deletions test/DeepStrictObjectKeys.ts

This file was deleted.

Loading

0 comments on commit 0166b92

Please sign in to comment.