feat(typescript-types): Allow inversion of filter in KeyPaths<T, O, F>
This commit is contained in:
@@ -1,3 +1,8 @@
|
|||||||
|
import type { If } from './if.js'
|
||||||
|
import type { Or } from './or.js'
|
||||||
|
import type { And } from './and.js'
|
||||||
|
import type { Not } from './not.js'
|
||||||
|
import type { IsEmptyString } from './is-empty-string.js'
|
||||||
import type { Extends } from './extends.js'
|
import type { Extends } from './extends.js'
|
||||||
import type { Concat } from './concat.js'
|
import type { Concat } from './concat.js'
|
||||||
import type { Assign } from './assign.js'
|
import type { Assign } from './assign.js'
|
||||||
@@ -20,15 +25,21 @@ interface KeyPaths_DefaultOptions {
|
|||||||
type ExtendsFilter<Obj, Key extends keyof Obj, Filter> =
|
type ExtendsFilter<Obj, Key extends keyof Obj, Filter> =
|
||||||
Extends<Obj[Key], Filter> extends false ? false : true
|
Extends<Obj[Key], Filter> extends false ? false : true
|
||||||
|
|
||||||
type IncludeElement<Obj, Key extends keyof Obj, Filter> = Obj extends never
|
type IncludeElement<Obj, Key extends keyof Obj, Filter, Options extends RequiredOptions> = Obj extends never
|
||||||
? false
|
? false
|
||||||
: Obj[Key] extends never
|
: Obj[Key] extends never
|
||||||
? false
|
? false
|
||||||
: unknown extends Obj[Key]
|
: unknown extends Obj[Key]
|
||||||
? false
|
? false
|
||||||
: ExtendsFilter<Obj, Key, Filter> extends false
|
: Not<
|
||||||
? true
|
Or<
|
||||||
: false
|
And<ExtendsFilter<Obj, Key, Filter>, Not<Options['invertFilter']>>,
|
||||||
|
And<
|
||||||
|
And<Not<ExtendsFilter<Obj, Key, Filter>>, Options['invertFilter']>,
|
||||||
|
Not<Obj[Key] extends object ? true : false>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
|
||||||
type AdaptKeyForOptionals<Obj, Key extends keyof Obj> = Key extends number | string
|
type AdaptKeyForOptionals<Obj, Key extends keyof Obj> = Key extends number | string
|
||||||
? undefined extends Obj[Key]
|
? undefined extends Obj[Key]
|
||||||
@@ -41,6 +52,10 @@ type AdaptKeyForOptionals<Obj, Key extends keyof Obj> = Key extends number | str
|
|||||||
type NonUndefined<Obj> = Obj extends undefined ? never : Obj
|
type NonUndefined<Obj> = Obj extends undefined ? never : Obj
|
||||||
type KeysOf<Obj> = Obj extends undefined ? never : Exclude<keyof Obj, keyof any[]>
|
type KeysOf<Obj> = Obj extends undefined ? never : Exclude<keyof Obj, keyof any[]>
|
||||||
|
|
||||||
|
type GetCurrentPrefixedKey<Parent extends string, Key extends string, Options extends RequiredOptions> =
|
||||||
|
| Concat<Parent, Key, Options['separator']>
|
||||||
|
| If<Or<Options['leavesOnly'], IsEmptyString<Parent>>, never, Parent>
|
||||||
|
|
||||||
type KeyPathsOfStringKeys<
|
type KeyPathsOfStringKeys<
|
||||||
Obj,
|
Obj,
|
||||||
Key extends keyof Obj,
|
Key extends keyof Obj,
|
||||||
@@ -48,9 +63,9 @@ type KeyPathsOfStringKeys<
|
|||||||
Filter,
|
Filter,
|
||||||
Parent extends string = ''
|
Parent extends string = ''
|
||||||
> = Key extends Key
|
> = Key extends Key
|
||||||
? IncludeElement<Obj, Key, Filter> extends true
|
? IncludeElement<Obj, Key, Filter, Options> extends true
|
||||||
? Obj[Key] extends NonContainerType
|
? Obj[Key] extends NonContainerType
|
||||||
? Concat<Parent, AdaptKeyForOptionals<Obj, Key>, Options['separator']>
|
? GetCurrentPrefixedKey<Parent, AdaptKeyForOptionals<Obj, Key>, Options>
|
||||||
: KeyPathsOfStringKeys<
|
: KeyPathsOfStringKeys<
|
||||||
NonUndefined<Obj[Key]>,
|
NonUndefined<Obj[Key]>,
|
||||||
KeysOf<Obj[Key]>,
|
KeysOf<Obj[Key]>,
|
||||||
|
|||||||
@@ -9,26 +9,76 @@ interface ExampleObject {
|
|||||||
undefinedKey: undefined
|
undefinedKey: undefined
|
||||||
undefinedOptionalKey?: undefined
|
undefinedOptionalKey?: undefined
|
||||||
optionalKey?: string
|
optionalKey?: string
|
||||||
|
objectKey: {
|
||||||
|
name: string
|
||||||
|
age?: number
|
||||||
|
}
|
||||||
|
optionalObjectKey?: {
|
||||||
|
name: string
|
||||||
|
age?: number
|
||||||
|
}
|
||||||
arrayKey: [{ name: string; age: number }, 1]
|
arrayKey: [{ name: string; age: number }, 1]
|
||||||
optionalArrayKey?: [{ name: string }, { age?: number }]
|
optionalArrayKey?: [{ name: string }, { age?: number }]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type KeysWithDefaultSettings =
|
||||||
|
| 'simplevalue'
|
||||||
|
| 'objectKey.name'
|
||||||
|
| 'arrayKey.0.name'
|
||||||
|
| 'arrayKey.0.age'
|
||||||
|
| 'arrayKey.1'
|
||||||
|
|
||||||
|
type KeysWithInvertFilterSettings =
|
||||||
|
| 'nullvalue'
|
||||||
|
| 'undefinedKey'
|
||||||
|
| 'undefinedOptionalKey'
|
||||||
|
| 'optionalKey?'
|
||||||
|
| 'objectKey.age?'
|
||||||
|
| 'optionalObjectKey?.age?'
|
||||||
|
| 'optionalArrayKey?.1.age?'
|
||||||
|
|
||||||
|
type KeysWithSpecialSeparator =
|
||||||
|
| 'simplevalue'
|
||||||
|
| 'objectKey-name'
|
||||||
|
| 'arrayKey-0-name'
|
||||||
|
| 'arrayKey-0-age'
|
||||||
|
| 'arrayKey-1'
|
||||||
|
|
||||||
type KeysWhenFilterIsSetToNever =
|
type KeysWhenFilterIsSetToNever =
|
||||||
| 'nullvalue'
|
| 'nullvalue'
|
||||||
| 'simplevalue'
|
| 'simplevalue'
|
||||||
| 'undefinedKey'
|
| 'undefinedKey'
|
||||||
| 'undefinedOptionalKey'
|
| 'undefinedOptionalKey'
|
||||||
| 'optionalKey?'
|
| 'optionalKey?'
|
||||||
|
| 'objectKey.name'
|
||||||
|
| 'objectKey.age?'
|
||||||
|
| 'optionalObjectKey?.name'
|
||||||
|
| 'optionalObjectKey?.age?'
|
||||||
| 'arrayKey.0.name'
|
| 'arrayKey.0.name'
|
||||||
| 'arrayKey.0.age'
|
| 'arrayKey.0.age'
|
||||||
| 'arrayKey.1'
|
| 'arrayKey.1'
|
||||||
| 'optionalArrayKey?.0.name'
|
| 'optionalArrayKey?.0.name'
|
||||||
| 'optionalArrayKey?.1.age?'
|
| 'optionalArrayKey?.1.age?'
|
||||||
|
|
||||||
|
type KeysWithLeavesOnlyDisabled =
|
||||||
|
| 'simplevalue'
|
||||||
|
| 'objectKey'
|
||||||
|
| 'objectKey.name'
|
||||||
|
| 'arrayKey'
|
||||||
|
| 'arrayKey.0'
|
||||||
|
| 'arrayKey.0.name'
|
||||||
|
| 'arrayKey.0.age'
|
||||||
|
| 'arrayKey.1'
|
||||||
|
|
||||||
// type T = any extends never ? true : false
|
// type T = any extends never ? true : false
|
||||||
// type A = KeyPaths<ExampleObject, {}, never>
|
// type A = KeyPaths<ExampleObject, { invertFilter: true }>
|
||||||
expect<KeyPaths<ExampleObject>>().type.toBe<
|
expect<KeyPaths<ExampleObject>>().type.toBe<KeysWithDefaultSettings>()
|
||||||
'simplevalue' | 'arrayKey.0.name' | 'arrayKey.0.age' | 'arrayKey.1'
|
expect<KeyPaths<ExampleObject, { invertFilter: true }>>().type.toBe<KeysWithInvertFilterSettings>()
|
||||||
>()
|
expect<KeyPaths<ExampleObject, { separator: '-' }>>().type.toBe<KeysWithSpecialSeparator>()
|
||||||
|
expect<KeyPaths<ExampleObject, { leavesOnly: false }>>().type.toBe<KeysWithLeavesOnlyDisabled>()
|
||||||
|
|
||||||
expect<KeyPaths<ExampleObject, {}, never>>().type.toBe<KeysWhenFilterIsSetToNever>()
|
expect<KeyPaths<ExampleObject, {}, never>>().type.toBe<KeysWhenFilterIsSetToNever>()
|
||||||
expect<KeyPaths<ExampleObject, {}, any>>().type.toBeAssignableWith<never>()
|
expect<KeyPaths<ExampleObject, {}, any>>().type.toBeAssignableWith<never>()
|
||||||
|
|
||||||
|
expect<KeyPaths<ExampleObject, { invertFilter: true }, never>>().type.toBeAssignableWith<never>()
|
||||||
|
expect<KeyPaths<ExampleObject, { invertFilter: true }, any>>().type.toBe<KeysWhenFilterIsSetToNever>()
|
||||||
|
|||||||
Reference in New Issue
Block a user