feat(typescript-types): Add Get<O, P>

This commit is contained in:
T. R. Bernstein
2025-07-18 13:29:54 +02:00
parent 5461fc52b4
commit ac53d1e584
3 changed files with 46 additions and 0 deletions

View File

@@ -46,12 +46,14 @@ The types included in this library are categorized by their purpose.
| Type | Description |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`Get<O, P>`][] | Extract the type of the item at keypath `P` in object `O`. |
| [`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]. |
| [`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'.
[`Get<O, P>`]: src/get.ts
[`KeyPaths<T, O, F>`]: src/key-paths.ts
[`OptionalKeysOf<T>`]: src/optional-keys-of.ts
[`PickAssignable<T, K>`]: src/pick-assignable.ts

View File

@@ -0,0 +1,25 @@
import type { If } from './if.js'
import type { KeyPath } from './key-path.js'
import type { KeyPaths } from './key-paths.js'
type FilterUndefined<T> = T extends undefined ? never : T
type FilterNull<T> = T extends null ? never : T
type FilterUndefinedAndNull<T> = FilterUndefined<FilterNull<T>>
type ExtractFromForKey<Obj, Key, Rest> = Key extends keyof Obj
? Obj[Key] extends Record<PropertyKey, unknown>
? GetByKeyPath<Obj[Key], Rest>
: Obj[Key]
: Key extends keyof FilterUndefinedAndNull<Obj>
? FilterUndefinedAndNull<Obj>[Key] | undefined
: undefined
type HasNoMoreKeys<K> = K extends [] ? true : false
type GetByKeyPath<Obj, Keys> = If<
HasNoMoreKeys<Keys>,
Obj,
Keys extends [infer Key, ...infer Rest] ? ExtractFromForKey<Obj, Key, Rest> : never
>
export type Get<O extends object, P extends KeyPaths<O>> = GetByKeyPath<O, KeyPath<P>>

View File

@@ -0,0 +1,19 @@
import type { Get } from '@/get.js'
import { expect } from 'tstyche'
interface Example1 {
requiredKey: number
optionalKey?: number
unknownNonOptionalKey: unknown
undefinedNonOptionalKey: undefined
neverKey: never
subkey: {
name: string
age: number
}
}
expect<Get<Example1, 'requiredKey'>>().type.toBe<number>()
expect<Get<Example1, 'optionalKey'>>().type.toBe<number | undefined>()
expect<Get<Example1, 'subkey.age'>>().type.toBe<number>()
expect<Get<Example1, 'unknownNonOptionalKey'>>().type.toBe<unknown>()