extraction
Key extraction utilities — optional, required, readonly, mutable, by-type.
import type {
KeysOfType, OptionalKeys, RequiredKeys, ReadonlyKeys, MutableKeys,
} from '@gentleduck/ttest/extraction'
Per-key arithmetic on object types. The complement of predicates — those check the whole object, these extract the keys that satisfy each modifier.
KeysOfType
type KeysOfType<O, T>
Keys of O whose value extends T.
type O = { a: number; b: string; c: number; d: boolean }
type k = KeysOfType<O, number> // 'a' | 'c'
// Pick by value type.
type StringKeys = KeysOfType<O, string> // 'b'
type Indexable = KeysOfType<O, number | string> // 'a' | 'b' | 'c'
Edge case: distribution over a union in T is intentional — KeysOfType<O, number | string> returns keys whose value matches either side.
OptionalKeys
type OptionalKeys<T>
Keys that are explicitly optional (declared with ?).
type T = { a: number; b?: string; c?: boolean }
type k = OptionalKeys<T> // 'b' | 'c'
import type { AssertTrue } from '@gentleduck/ttest/assert'
import type { Equal } from '@gentleduck/ttest/equality'
type _ = AssertTrue<Equal<OptionalKeys<{ id: number; name?: string }>, 'name'>, 'name is the only optional key'>
RequiredKeys
type RequiredKeys<T> = Exclude<keyof T, OptionalKeys<T>>
Keys that are not optional.
type T = { a: number; b?: string }
type k = RequiredKeys<T> // 'a'
// Constrain a function to receive only the required slice.
function requireFields<T>(obj: T): Pick<T, RequiredKeys<T>> {
return obj as Pick<T, RequiredKeys<T>>
}
ReadonlyKeys
type ReadonlyKeys<T>
Keys marked readonly.
type T = { readonly a: number; b: string; readonly c: boolean }
type k = ReadonlyKeys<T> // 'a' | 'c'
MutableKeys
type MutableKeys<T> = Exclude<keyof T, ReadonlyKeys<T>>
Keys not marked readonly.
type T = { readonly a: number; b: string }
type k = MutableKeys<T> // 'b'
Edge case for all key extractors: keyof any (i.e. an index signature) is treated as one key, not as the full set. OptionalKeys<Record<string, unknown>> is string — the index-signature key — which can surprise downstream consumers. Use OmitIndexSignature first if you want only explicit keys.