Expressive MVC
API Reference

Types

TypeScript type aliases on the Model namespace

Most public types are namespaced on Model; component prop types are namespaced on Component. Import them from @expressive/mvc:

import type { Model } from '@expressive/mvc';

Class shape

Model.Extends<T>

Abstract or concrete constructor of a Model. Use for parameters that accept any class in the hierarchy.

function lookup<T extends Model>(Type: Model.Extends<T>): T | undefined;

Model.Type<T>

Concrete (instantiable) constructor. Use when new is required.

function create<T extends Model>(Type: Model.Type<T>): T;

Field introspection

Model.Property<T>

Keys of T excluding inherited Model members (get, set, is, etc.). This includes unmanaged _ keys, so it's the key set for overlays like Model.Assign<T> and component props.

Model.Field<T>

Managed keys only - Model.Property<T> without _ keys. Snapshots (Model.Values<T>), ref proxies and instruction keys use this.

class FormState extends Model {
  foo = 1;
  bar = 'hello';
  _handle = null;
}

type Properties = Model.Property<FormState>; // 'foo' | 'bar' | '_handle'
type Fields = Model.Field<FormState>; // 'foo' | 'bar'

Model.Event<T>

Any valid event key - includes fields, numbers, symbols, and arbitrary strings for custom events.

type Event<T> = Field<T> | number | symbol | (string & {});

Model.Signal<T>

All event signals dispatched to listeners, including lifecycle meta events.

type Signal<T> = Event<T> | true | false | null;

Meaning of special values:

  • true - initial activation (ready)
  • false - a normal update flush completed
  • null - destruction

Values and snapshots

Model.Values<T>

All fields with exotic values (child instances, refs, etc.) unwrapped to their inner type.

type Values<T> = { [P in Field<T>]: Export<T[P]> };

Model.Partial<T>

Object compatible with Values<T> - every key optional, Export-unwrapped.

type Partial<T> = { [P in Field<T>]?: Export<T[P]> };

Not currently used by any public signature - set(assign) takes Model.Assign<T>, which preserves declared field types rather than unwrapping them.

Model.Value<T, K>

Single-property lookup type with Export<> unwrapping.

Model.Export<R>

If R is itself a Model, recursively exports Values<R>. Otherwise, if R has a .get(): T method, extracts T. Otherwise returns R. Drives child-instance and ref unwrapping in snapshots.

type Export<R> = R extends Model
  ? Values<R>
  : R extends { get(): infer T }
  ? T
  : R;

Constructor args

Model.Args<T>

Union type accepted by Model.new():

type Args<T> = (Args<T> | Init<T> | Assign<T> | Context | void)[];

Arrays are flattened recursively at runtime. A Context claims the home - the first one wins, unless the instance was already placed.

Model.Init<T>

A lifecycle callback passed to Model.new(). Can return cleanup, object (assign), or array (recurse).

type Init<T> = (
  this: T,
  thisArg: T
) => Promise<void> | (() => void) | Args<T> | Assign<T> | void;

Model.On<T>

Handler object accepted by Model.on() - hooks lifecycle by stage.

interface On<T> {
  type?(type: Model.Extends<T>): void;
  setup?(this: T, self: T): void | (() => void) | Promise<void> | Args<T> | Assign<T>;
  ready?(this: T, self: T): void | (() => void);
  method?(this: T, key: string, fn: Function, self: T): void;
  catch?(
    this: T,
    error: unknown,
    kind: 'effect' | 'getter' | 'setup' | 'dead' | 'unused',
    key?: string
  ): unknown;
}

Stage timing is covered under Model.on; catch kinds under Error handling.

Model.Ref<T>

The exotic value a ref() field holds - callable to assign, with the value on current:

type Ref<T = any> = {
  (next: T): void;
  current: T | null;
};

Model.Assign<T>

Object overlay type. Maps each field to its value type, preserving method this.

type Assign<T> = Record<string, unknown> & {
  [K in Property<T>]?: T[K] extends (...args: infer A) => infer R
    ? (this: T, ...args: A) => R
    : T[K];
};

The Record<string, unknown> intersection means unknown keys pass the type-checker (they're ignored at runtime) - value-correctness of declared fields is the only check. See What Assign checks.


Callbacks

Model.OnEvent<T>

type OnEvent<T> = (
  this: T,
  key: Signal<T>,
  source: T
) => void | (() => void) | null;

Return a function for a deduped settle callback. Return null to auto-unsubscribe.

Model.Effect<T>

type Effect<T, R = EffectCallback | Promise<void> | null | void> = (
  this: T,
  current: T,
  update: readonly Event<T>[] | undefined
) => R;

type EffectCallback = (update: boolean | null) => void;

Model.Release<R>

type Release<R = void> = (() => void) & PromiseLike<R>;

Returned by get(effect). Call to stop the effect; await to stop it once it settles and receive the latest run's value. A sync effect returns its cleanup, so only an async effect resolves a value.

Model.Setter<T>

type Setter<T> = (value: T, previous: T) => T | void;

Descriptors

Model.Apply<T>

Property descriptor config for set(key, config) and def factories:

type Apply<T> = {
  value?: T;
  get?: ((source: Model) => T) | boolean;
  set?: Setter<T> | boolean;
  enumerable?: boolean;
};

Model.Define<T, K>

Type-safe variant of Apply used by set(key, config) - enforces T[K] when K is a known field.


Updated

Model.Updated<T>

type Updated<T> = readonly Event<T>[] & PromiseLike<readonly Event<T>[]>;

Returned by set() - a synchronously readable array of keys updated, awaitable to wait for batch settlement.


Refs

ref.Object<T>

interface ref.Object<T> {
  (value: T | null): void;
  current: T;
  is: Model;
  key: string;
  get(): T | null;
  get(callback: (v: T) => void): () => void;
}

ref.Proxy<T> and ref.CustomProxy<T, R>

Produced by ref(this) and ref(this, mapFn) respectively.

type ref.Proxy<T extends Model> =
  { [P in Model.Field<T>]-?: ref.Object<T[P]> } & { get(): T };

type ref.CustomProxy<T extends Model, R> =
  { [P in Model.Field<T>]-?: R } & { get(): T };

Component

Component.Node

The host's element type - ReactNode once @expressive/react is loaded. With no host loaded it is any, so an un-annotated render() in a host-agnostic package still type-checks as JSX.

Component.ModelProps<T>

Every settable field of the class (getters and readonly excluded), each optional - the attributes a parent may assign on every render.

Component.BaseProps<T>

The special props: is ((instance: T) => Model.Assign<T> | void, run once; a returned object is assigned), fallback (Component.Node, or false to defer to an ancestor boundary) and catch ((error, instance) => Promise<void> | void).

Component.RenderProps<R>

The props a render(props) declaration adds - its parameter type when one is declared, else { children?: Component.Node }.

Component.Props<T>

The full JSX prop type for a Component subclass. Combines:

  • Model fields (optional)
  • render(props) declared props (optional or required as declared), or children when render declares none
  • is, fallback and catch special props

On this page