object
Deep transforms, key arithmetic, dotted-path access, case conversion — the broadest module.
import type {
Simplify, Prettify, Override, Assign,
DeepPartial, DeepRequired, DeepReadonly, DeepMutable, DeepWritable, DeepNonNullable,
Mutable, Writable,
PartialBy, RequiredBy, ReadonlyBy, MutableBy,
StrictOmit, StrictPick, Except,
ValueOf, Entries, FromEntries, Invert, RenameKey,
RequireAtLeastOne, RequireExactlyOne, RequireAllOrNone, RequireOneOrNone,
NonEmptyObject,
DeepPick, DeepOmit, Get, Paths,
MergeDeep, MergeAll, OverrideProperties,
OmitIndexSignature, PickIndexSignature, KeysOfUnion,
Schema, SetOptional, SetRequired, SetReadonly, SetMutable,
OmitNever, Without, ReplaceValue, DeepReplaceValue,
PickRequired, PickOptional,
ConditionalPick, ConditionalExcept, PickByType, OmitByType,
MergeExclusive,
CamelCaseKeys, PascalCaseKeys, SnakeCaseKeys, KebabCaseKeys,
DeepCamelCaseKeys, DeepPascalCaseKeys, DeepSnakeCaseKeys, DeepKebabCaseKeys,
Pluck, SetKeyType, SharedKeys, DiffKeys,
HasSameKeys, CommonKeys, ConditionalKeys,
DistributedOmit, DistributedPick,
EmptyObject, UnknownRecord, IsEmptyRecord,
} from '@gentleduck/ttest/object'
The largest module. Grouped below by concern.
Simplification
type Simplify<T> = { [K in keyof T]: T[K] } & {}
type Prettify<T> = Simplify<T>
Flatten an intersection into a single object — improves IDE hover output.
type a = Simplify<{ a: 1 } & { b: 2 }> // { a: 1; b: 2 }
Override / Assign
type Override<A, B> = Simplify<Omit<A, keyof B> & B>
type Assign<A, B> = Override<A, B>
Override properties of A with B. Keys in B win.
Deep transforms
type DeepPartial <T> // every property recursively optional
type DeepRequired <T> // every property recursively required
type DeepReadonly <T> // every property recursively readonly
type DeepMutable <T> // strip readonly recursively
type DeepWritable <T> = DeepMutable<T>
type DeepNonNullable<T> // strip null|undefined recursively
All stop at the Builtin lattice (Date, Map, Set, Promise, etc.) — they don't recurse into class instances.
Shallow mutability
type Mutable <T> // -readonly on all top-level keys
type Writable<T> = Mutable<T>
Per-key modifiers
type PartialBy <T, K extends keyof T>
type RequiredBy<T, K extends keyof T>
type ReadonlyBy<T, K extends keyof T>
type MutableBy <T, K extends keyof T>
Mark some keys with the modifier; leave the rest untouched.
type T = PartialBy<{ a: 1; b: 2 }, 'a'> // { a?: 1; b: 2 }
SetOptional / SetRequired / SetReadonly / SetMutable are aliases for type-fest parity.
Strict Pick / Omit
type StrictOmit<T, K extends keyof T> = Omit<T, K>
type StrictPick<T, K extends keyof T> = Pick<T, K>
type Except <T, K extends keyof T> = Omit<T, K>
Like Omit / Pick but constrained — K must be a real key of T.
Values, entries, key arithmetic
type ValueOf<T> = T[keyof T]
type Entries<T> // union of [key, value] tuples
type FromEntries<T extends readonly [PropertyKey, unknown]>
type Invert<T extends Record<PropertyKey, PropertyKey>>
type RenameKey<T, From extends keyof T, To extends PropertyKey>
RequireAtLeastOne / ExactlyOne / AllOrNone / OneOrNone
Combinatorial key constraints. See examples in the source.
type T = { a?: 1; b?: 2; c?: 3 }
type r = RequireAtLeastOne<T, 'a' | 'b'>
// → { a: 1; b?: 2; c?: 3 } | { a?: 1; b: 2; c?: 3 }
Dotted paths
type DeepPick<T, Path extends string>
type DeepOmit<T, Path extends string>
type Get <T, Path extends string>
type Paths <T, D extends number = 6>
Operate on 'a.b.c'-style dotted paths. Paths<T, D> is depth-capped at D = 6 by default — lower for very wide objects.
type T = { a: { b: { c: number } } }
type x = Get <T, 'a.b.c'> // number
type y = DeepPick<T, 'a.b'> // { a: { b: { c: number } } }
type p = Paths<T> // 'a' | 'a.b' | 'a.b.c'
Merges
type MergeDeep<A, B>
type MergeAll <T extends readonly object[]>
type OverrideProperties<A, B extends Partial<Record<keyof A, unknown>>>
Index signature surgery
type OmitIndexSignature<T> // keep only explicit keys
type PickIndexSignature<T> // keep only the index signature
type KeysOfUnion<T> // union of every key present in any union member
Schema / Replace
type Schema<T, V> // replace every leaf with V
type OmitNever<T> // drop never-valued keys
type Without<T, V> // drop keys whose value extends V
type ReplaceValue<T, From, To>
type DeepReplaceValue<T, From, To>
Required / Optional pickers
type PickRequired<T>
type PickOptional<T>
type ConditionalPick <T, U> // keys whose value extends U
type ConditionalExcept<T, U>
type PickByType<T, U> = ConditionalPick<T, U>
type OmitByType<T, U> = ConditionalExcept<T, U>
MergeExclusive
type MergeExclusive<A, B> // A xor B
Mutually-exclusive merge — exactly one shape wins.
Case conversion
Shallow and deep variants for camelCase, PascalCase, snake_case, kebab-case:
type CamelCaseKeys <T>
type PascalCaseKeys <T>
type SnakeCaseKeys <T>
type KebabCaseKeys <T>
type DeepCamelCaseKeys <T>
type DeepPascalCaseKeys <T>
type DeepSnakeCaseKeys <T>
type DeepKebabCaseKeys <T>
type T = { user_id: number; full_name: string }
type k = CamelCaseKeys<T> // { userId: number; fullName: string }
Misc
type Pluck<T, K extends keyof T> // T[K]
type SetKeyType<T, K, V> // replace T[K] with V
type SharedKeys<A, B> // keys in both
type DiffKeys <A, B> // keys in A but not B
type HasSameKeys<A, B> // boolean
type CommonKeys<U> // keys in every union member
type ConditionalKeys<T, Condition> // keys whose value extends Condition
type DistributedOmit<T, K extends keyof any>
type DistributedPick<T, K extends keyof any>
type EmptyObject = Record<PropertyKey, never>
type UnknownRecord = Record<PropertyKey, unknown>
type IsEmptyRecord<T>
type NonEmptyObject<T>
DistributedOmit / DistributedPick preserve discriminated unions where the built-in Omit / Pick collapse them.