feat(typescript-types): Allow inversion of filter in KeyPaths<T, O, F>

This commit is contained in:
T. R. Bernstein
2025-07-18 19:56:44 +02:00
parent 520a690283
commit 0e7238998d
2 changed files with 20 additions and 8 deletions

View File

@@ -7,11 +7,13 @@ import type { Assign } from './assign.js'
interface KeyPaths_Options { interface KeyPaths_Options {
separator?: string separator?: string
leavesOnly?: boolean leavesOnly?: boolean
invertFilter?: boolean
} }
interface KeyPaths_DefaultOptions { interface KeyPaths_DefaultOptions {
separator: '.' separator: '.'
leavesOnly: true leavesOnly: true
invertFilter: false
} }
type GetPrefixedKey< type GetPrefixedKey<
@@ -34,8 +36,16 @@ type KeyPathsOfStringKeys<
Parent extends string = '' Parent extends string = ''
> = { > = {
[Key in keyof Obj & string]: Obj[Key] extends Filter [Key in keyof Obj & string]: Obj[Key] extends Filter
? never ? If<
: KeyPathOf<Obj[Key], Options, GetPrefixedKey<Parent, Key, Options['separator']>, Filter> Options['invertFilter'],
KeyPathOf<Obj[Key], Options, GetPrefixedKey<Parent, Key, Options['separator']>, Filter>,
never
>
: If<
Options['invertFilter'],
never,
KeyPathOf<Obj[Key], Options, GetPrefixedKey<Parent, Key, Options['separator']>, Filter>
>
}[keyof Obj & string] }[keyof Obj & string]
export type KeyPaths< export type KeyPaths<

View File

@@ -16,12 +16,7 @@ interface ExampleObject {
} }
} }
expect<KeyPaths<ExampleObject>>().type.toBe< type AllKeys =
'simplevalue' | 'optionalKey.simplevalue' | 'unknownKey' | 'config.simplevalue'
>()
expect<KeyPaths<ExampleObject, {}, any>>().type.toBeAssignableTo<never>()
expect<KeyPaths<ExampleObject, {}, unknown>>().type.toBeAssignableTo<never>()
expect<KeyPaths<ExampleObject, {}, never>>().type.toBe<
| 'nullvalue' | 'nullvalue'
| 'simplevalue' | 'simplevalue'
| 'optionalKey.nullvalue' | 'optionalKey.nullvalue'
@@ -29,7 +24,14 @@ expect<KeyPaths<ExampleObject, {}, never>>().type.toBe<
| 'unknownKey' | 'unknownKey'
| 'config.nullvalue' | 'config.nullvalue'
| 'config.simplevalue' | 'config.simplevalue'
expect<KeyPaths<ExampleObject>>().type.toBe<
'simplevalue' | 'optionalKey.simplevalue' | 'unknownKey' | 'config.simplevalue'
>() >()
expect<KeyPaths<ExampleObject, {}, any>>().type.toBeAssignableTo<never>()
expect<KeyPaths<ExampleObject, {}, unknown>>().type.toBeAssignableTo<never>()
expect<KeyPaths<ExampleObject, {}, never>>().type.toBe<AllKeys>()
expect<KeyPaths<ExampleObject, { invertFilter: true }, any>>().type.toBe<AllKeys>()
expect<KeyPaths<ExampleObject, {}, null>>().type.toBe< expect<KeyPaths<ExampleObject, {}, null>>().type.toBe<
'simplevalue' | 'optionalKey.simplevalue' | 'unknownKey' | 'config.simplevalue' 'simplevalue' | 'optionalKey.simplevalue' | 'unknownKey' | 'config.simplevalue'
>() >()