feat(typescript-types): Adapt Get to KeyPaths and KeyPath changes
This commit is contained in:
@@ -1,25 +1,30 @@
|
|||||||
import type { If } from './if.js'
|
|
||||||
import type { KeyPath } from './key-path.js'
|
import type { KeyPath } from './key-path.js'
|
||||||
import type { KeyPaths } from './key-paths.js'
|
import type { NonContainerType } from './non-container-type.js'
|
||||||
|
import type { KeyPaths, KeyPaths_Options } from './key-paths.js'
|
||||||
|
|
||||||
type FilterUndefined<T> = T extends undefined ? never : T
|
type ExtractFromForKey<Obj, Rest, ResultType = never> = Obj extends unknown
|
||||||
type FilterNull<T> = T extends null ? never : T
|
? never
|
||||||
type FilterUndefinedAndNull<T> = FilterUndefined<FilterNull<T>>
|
: Obj extends undefined
|
||||||
|
? never
|
||||||
|
: Obj extends NonContainerType
|
||||||
|
? Obj | ResultType
|
||||||
|
: GetByKeyPath<Obj, Rest, ResultType>
|
||||||
|
|
||||||
type ExtractFromForKey<Obj, Key, Rest> = Key extends keyof Obj
|
type GetByKeyPath<Obj, Keys, ResultType = never> = Keys extends []
|
||||||
? Obj[Key] extends Record<PropertyKey, unknown>
|
? Obj
|
||||||
? GetByKeyPath<Obj[Key], Rest>
|
: Keys extends [infer Key, ...infer Rest]
|
||||||
: Obj[Key]
|
? Key extends keyof Obj
|
||||||
: Key extends keyof FilterUndefinedAndNull<Obj>
|
? ExtractFromForKey<Obj[Key], Rest, ResultType>
|
||||||
? FilterUndefinedAndNull<Obj>[Key] | undefined
|
: Key extends `${infer Key}?`
|
||||||
: undefined
|
? Key extends keyof Obj
|
||||||
|
? ExtractFromForKey<Obj[Key], Rest, ResultType | undefined>
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
: never
|
||||||
|
|
||||||
type HasNoMoreKeys<K> = K extends [] ? true : false
|
export type Get<
|
||||||
|
O,
|
||||||
type GetByKeyPath<Obj, Keys> = If<
|
P extends KeyPaths<O, Options, Filter>,
|
||||||
HasNoMoreKeys<Keys>,
|
Options extends KeyPaths_Options = {},
|
||||||
Obj,
|
Filter = null | undefined
|
||||||
Keys extends [infer Key, ...infer Rest] ? ExtractFromForKey<Obj, Key, Rest> : never
|
> = GetByKeyPath<O, KeyPath<P>>
|
||||||
>
|
|
||||||
|
|
||||||
export type Get<O extends object, P extends KeyPaths<O>> = GetByKeyPath<O, KeyPath<P>>
|
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
import type { Get } from '@/get.js'
|
import type { Get } from '@/get.js'
|
||||||
import { expect } from 'tstyche'
|
import { expect } from 'tstyche'
|
||||||
|
|
||||||
interface Example1 {
|
interface ExampleObject {
|
||||||
requiredKey: number
|
nullvalue: null
|
||||||
optionalKey?: number
|
simplevalue: string
|
||||||
unknownNonOptionalKey: unknown
|
unknownKey: unknown
|
||||||
undefinedNonOptionalKey: undefined
|
|
||||||
neverKey: never
|
neverKey: never
|
||||||
subkey: {
|
undefinedKey: undefined
|
||||||
|
undefinedOptionalKey?: undefined
|
||||||
|
optionalKey?: string
|
||||||
|
objectKey: {
|
||||||
name: string
|
name: string
|
||||||
age: number
|
age?: number
|
||||||
}
|
}
|
||||||
|
optionalObjectKey?: {
|
||||||
|
name: string
|
||||||
|
age?: number
|
||||||
|
}
|
||||||
|
arrayKey: [{ name: string; age: number }, 1]
|
||||||
|
optionalArrayKey?: [{ name: string }, { age?: number }]
|
||||||
}
|
}
|
||||||
|
|
||||||
expect<Get<Example1, 'requiredKey'>>().type.toBe<number>()
|
// type T = any extends never ? true : false
|
||||||
expect<Get<Example1, 'optionalKey'>>().type.toBe<number | undefined>()
|
// type A = Get<ExampleObject, 'simplevalue'>
|
||||||
expect<Get<Example1, 'subkey.age'>>().type.toBe<number>()
|
expect<Get<ExampleObject, 'simplevalue'>>().type.toBe<string>()
|
||||||
expect<Get<Example1, 'unknownNonOptionalKey'>>().type.toBe<unknown>()
|
expect<Get<ExampleObject, 'optionalObjectKey?.name', {}, never>>().type.toBe<string | undefined>()
|
||||||
|
expect<Get<ExampleObject, 'optionalObjectKey?.age?', {}, never>>().type.toBe<number | undefined>()
|
||||||
|
expect<Get<ExampleObject, 'optionalArrayKey?.0.name', {}, never>>().type.toBe<string | undefined>()
|
||||||
|
|||||||
Reference in New Issue
Block a user