Skip to main content

predicates

Property-modifier guards — IsMutable, IsReadonly, IsPartial, IsRequired.

import type {
  IsMutable, IsReadonly, IsPartial, IsRequired,
} from '@gentleduck/ttest/predicates'

Whole-object property-modifier checks. Use to assert "this object is fully optional" or "fully readonly" in tests and constraints. Built on top of Equal, so they perform variance-preserving comparison rather than one-way assignability.

IsMutable

type IsMutable<T> = Equal<{ -readonly [K in keyof T]: T[K] }, T>

true if every property of T is mutable (no readonly modifier on any key).

type a = IsMutable<{ a: 1; b: 2 }>                 // true
type b = IsMutable<{ readonly a: 1; b: 2 }>        // false
type c = IsMutable<{ readonly a: 1 }>              // false
import type { AssertTrue } from '@gentleduck/ttest/assert'
type _ = AssertTrue<IsMutable<{ count: number }>, 'count is mutable'>

IsReadonly

type IsReadonly<T> = Equal<T, Readonly<T>>

true if every property of T is readonly.

type a = IsReadonly<{ readonly a: 1; readonly b: 2 }>  // true
type b = IsReadonly<{ readonly a: 1; b: 2 }>           // false
type c = IsReadonly<{ a: 1 }>                          // false
// Constrain config objects to be immutable at the type level.
function freeze<T extends object>(value: T): T & (IsReadonly<T> extends true ? unknown : never) {
  return Object.freeze(value) as never
}

IsPartial

type IsPartial<T> = Equal<{ [K in keyof T]?: T[K] }, T>

true if every property of T is optional.

type a = IsPartial<{ a?: 1; b?: 2 }>     // true
type b = IsPartial<{ a: 1; b?: 2 }>      // false
type c = IsPartial<{}>                   // true   (vacuously)

IsRequired

type IsRequired<T> = Equal<{ [K in keyof T]-?: T[K] }, T>

true if every property of T is required.

type a = IsRequired<{ a: 1; b: 2 }>      // true
type b = IsRequired<{ a: 1; b?: 2 }>     // false
type c = IsRequired<{}>                  // true   (vacuously)

Edge cases for all four predicates:

  • Empty objects ({}) trivially pass every modifier check.
  • These are whole-object predicates. For per-key arithmetic see extractionOptionalKeys, RequiredKeys, ReadonlyKeys, MutableKeys.
  • IsRequired<any> and IsRequired<unknown> are not meaningful. Narrow before checking.