feat(typescript-types): Fully rewrite KeyPaths<T,O,F>
This commit is contained in:
@@ -46,13 +46,13 @@ The types included in this library are categorized by their purpose.
|
|||||||
|
|
||||||
| Type | Description |
|
| Type | Description |
|
||||||
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| [`KeyPaths<T, F, O>`][] | A union of all keypaths of `T` where the value does not extend `F`. Separator can be set via `O['separator']`. |
|
| [`KeyPaths<T, O, F>`][] | A union of all keypaths of `T` where the value does not extend `F`. Separator can be set via `O['separator']`. |
|
||||||
| [`OptionalKeysOf<T>`][] | A union of all keys of `T` that are marked as optional. If `T` is a union, a union of the optional keys of all union members of `T` is returned[^optional-keys-of_remark]. |
|
| [`OptionalKeysOf<T>`][] | A union of all keys of `T` that are marked as optional. If `T` is a union, a union of the optional keys of all union members of `T` is returned[^optional-keys-of_remark]. |
|
||||||
| [`PickAssignable<T, K>`][] | Return a mapped type with all keys of `T` that extend `K`. If no key does extend `K` an empty type is returned. |
|
| [`PickAssignable<T, K>`][] | Return a mapped type with all keys of `T` that extend `K`. If no key does extend `K` an empty type is returned. |
|
||||||
|
|
||||||
[^optional-keys-of_remark]: If `T` is `any`, it returns a union of string and number 'string | number'.
|
[^optional-keys-of_remark]: If `T` is `any`, it returns a union of string and number 'string | number'.
|
||||||
|
|
||||||
[`KeyPaths<T, F, O>`]: src/key-paths.ts
|
[`KeyPaths<T, O, F>`]: src/key-paths.ts
|
||||||
[`OptionalKeysOf<T>`]: src/optional-keys-of.ts
|
[`OptionalKeysOf<T>`]: src/optional-keys-of.ts
|
||||||
[`PickAssignable<T, K>`]: src/pick-assignable.ts
|
[`PickAssignable<T, K>`]: src/pick-assignable.ts
|
||||||
|
|
||||||
|
|||||||
@@ -22,28 +22,27 @@ type PrefixIfNot<Cond, Prefix, T> = If<Cond, T, Prefix | T>
|
|||||||
|
|
||||||
type KeyPathOf<
|
type KeyPathOf<
|
||||||
Obj,
|
Obj,
|
||||||
|
Key extends PropertyKey,
|
||||||
Options extends Required<KeyPaths_Options>,
|
Options extends Required<KeyPaths_Options>,
|
||||||
Parent extends string,
|
Parent extends string,
|
||||||
Filter = never
|
Filter = never
|
||||||
> = Obj extends object
|
> = Obj extends { [P in Key]: Record<PropertyKey, unknown> }
|
||||||
? PrefixIfNot<Options['leavesOnly'], Parent, KeyPathsOfStringKeys<Obj, Options, Filter, Parent>>
|
? PrefixIfNot<Options['leavesOnly'], Parent, KeyPathsOfStringKeys<Obj[Key], Options, Filter, Parent>>
|
||||||
: Parent
|
: Parent
|
||||||
|
|
||||||
type DoesNotMatchFilter<T, Filter> = T extends Filter ? false : true
|
|
||||||
|
|
||||||
type KeyPathsOfStringKeys<
|
type KeyPathsOfStringKeys<
|
||||||
Obj extends object,
|
Obj extends object,
|
||||||
Options extends Required<KeyPaths_Options>,
|
Options extends Required<KeyPaths_Options>,
|
||||||
Filter = never,
|
Filter,
|
||||||
Parent extends string = ''
|
Parent extends string = ''
|
||||||
> = {
|
> = {
|
||||||
[Key in keyof Obj & string]: DoesNotMatchFilter<Obj[Key], Filter> extends true
|
[Key in keyof Obj & string]: [Filter] extends [Exclude<Filter, Obj[Key]>]
|
||||||
? KeyPathOf<Obj[Key], Options, GetPrefixedKey<Parent, Key, Options['separator']>, Filter>
|
? KeyPathOf<Obj, Key, Options, GetPrefixedKey<Parent, Key, Options['separator']>, Filter>
|
||||||
: never
|
: never
|
||||||
}[keyof Obj & string]
|
}[keyof Obj & string]
|
||||||
|
|
||||||
export type KeyPaths<
|
export type KeyPaths<
|
||||||
Obj extends object,
|
Obj extends object,
|
||||||
Filter = null | undefined | never,
|
Options extends KeyPaths_Options = {},
|
||||||
Options extends KeyPaths_Options = {}
|
Filter = null | undefined
|
||||||
> = KeyPathsOfStringKeys<Obj, Assign<KeyPaths_Options, KeyPaths_DefaultOptions, Options>, Filter>
|
> = KeyPathsOfStringKeys<Obj, Assign<KeyPaths_Options, KeyPaths_DefaultOptions, Options>, Filter>
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ interface ExampleObject {
|
|||||||
nullvalue: null
|
nullvalue: null
|
||||||
simplevalue: string
|
simplevalue: string
|
||||||
optionalKey?: string
|
optionalKey?: string
|
||||||
|
unknownKey: unknown
|
||||||
|
neverKey: never
|
||||||
config: {
|
config: {
|
||||||
nullvalue: null
|
nullvalue: null
|
||||||
simplevalue: string
|
simplevalue: string
|
||||||
@@ -12,23 +14,26 @@ interface ExampleObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expect<KeyPaths<ExampleObject>>().type.toBe<'simplevalue' | 'config.simplevalue'>()
|
expect<KeyPaths<ExampleObject>>().type.toBe<'simplevalue' | 'config.simplevalue'>()
|
||||||
expect<KeyPaths<ExampleObject, never>>().type.toBe<
|
expect<KeyPaths<ExampleObject, {}, any>>().type.toBeAssignableTo<
|
||||||
'nullvalue' | 'simplevalue' | 'optionalKey' | 'config.nullvalue' | 'config.simplevalue'
|
'nullvalue' | 'simplevalue' | 'optionalKey' | 'config.nullvalue' | 'config.simplevalue'
|
||||||
>()
|
>()
|
||||||
expect<KeyPaths<ExampleObject, null>>().type.toBe<'simplevalue' | 'optionalKey' | 'config.simplevalue'>()
|
expect<KeyPaths<ExampleObject, {}, unknown>>().type.toBeAssignableTo<
|
||||||
expect<KeyPaths<ExampleObject, string>>().type.toBe<'nullvalue' | 'config.nullvalue'>()
|
'nullvalue' | 'simplevalue' | 'optionalKey' | 'config.nullvalue' | 'config.simplevalue'
|
||||||
|
|
||||||
expect<KeyPaths<ExampleObject, null, { separator: '-' }>>().type.toBe<
|
|
||||||
'simplevalue' | 'optionalKey' | 'config-simplevalue'
|
|
||||||
>()
|
>()
|
||||||
expect<KeyPaths<ExampleObject, null, { leavesOnly: false }>>().type.toBe<
|
expect<KeyPaths<ExampleObject, {}, never>>().type.toBe<
|
||||||
'simplevalue' | 'optionalKey' | 'config' | 'config.simplevalue'
|
'nullvalue' | 'simplevalue' | 'optionalKey' | 'unknownKey' | 'config.nullvalue' | 'config.simplevalue'
|
||||||
|
>()
|
||||||
|
expect<KeyPaths<ExampleObject, {}, null>>().type.toBe<'simplevalue' | 'optionalKey' | 'config.simplevalue'>()
|
||||||
|
expect<KeyPaths<ExampleObject, {}, string>>().type.toBe<'nullvalue' | 'config.nullvalue'>()
|
||||||
|
|
||||||
|
expect<KeyPaths<ExampleObject, { separator: '-' }>>().type.toBe<'simplevalue' | 'config-simplevalue'>()
|
||||||
|
expect<KeyPaths<ExampleObject, { leavesOnly: false }>>().type.toBe<
|
||||||
|
'simplevalue' | 'config' | 'neverKey' | 'config.simplevalue'
|
||||||
>()
|
>()
|
||||||
|
|
||||||
expect<KeyPaths<any, string>>().type.toBeAssignableTo<never>()
|
expect<KeyPaths<any, {}, string>>().type.toBeAssignableTo<never>()
|
||||||
expect<KeyPaths<any, never>>().type.toBeAssignableTo<never>()
|
expect<KeyPaths<any, {}, any>>().type.toBeAssignableTo<never>()
|
||||||
expect<KeyPaths<ExampleObject, any>>().type.toBeAssignableTo<never>()
|
expect<KeyPaths<never, {}, any>>().type.toBeAssignableTo<never>()
|
||||||
expect<KeyPaths<ExampleObject, unknown>>().type.toBeAssignableTo<never>()
|
expect<KeyPaths<never, {}, never>>().type.toBeAssignableTo<never>()
|
||||||
expect<KeyPaths<any, any>>().type.toBeAssignableTo<never>()
|
|
||||||
expect<KeyPaths<never, any>>().type.toBeAssignableTo<never>()
|
expect<KeyPaths<any, {}, never>>().type.toBe<unknown>()
|
||||||
expect<KeyPaths<never, never>>().type.toBeAssignableTo<never>()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user