Skip to content

Types

Utilities to derive the state, args, deps, and instance shapes of a container class without hand-writing them, plus branded-ID helpers for nominal instance-identity strings. All re-exported from @blac/core (source: types/utilities and types/branded). For a task-oriented walkthrough (generics, inference, typing select) see TypeScript.

A StateContainer (and Cubit) carries three type parameters — S (state), Args (serializable construction/identity data), Deps (injected handles) — extracted from a container class (e.g. typeof CounterCubit), not an instance.

TypeSignatureWhat it’s forWhere you’d use it
ExtractState<T>T extends StateContainerConstructor<infer S> ? Readonly<S> : neverThe state type useBloc and .state return — read-only.Typing a component prop as “this bloc’s state”.
ExtractStateMutable<T>Same, without Readonly<>The raw S as declared.Building a next-state object before emiting it.
ExtractArgs<T>T extends new () => StateContainer<any, infer A, any> ? A : voidThe serializable Args a bloc is constructed/identified with. Falls back to void.Typing the args option on useBloc/registry functions.
ExtractDeps<T>T extends new () => StateContainer<any, any, infer D> ? D : Record<string, never>The injected non-serializable Deps shape. Falls back to Record<string, never>.Typing the deps option on useBloc.
ExtractConstructorArgs<T>T extends new (...args: infer P) => any ? P : never[]The literal runtime constructor(...) parameter tuple — plain TS inference, not BlaC’s Args.Rare; typing a wrapper around a class’s real constructor.
import {
class Cubit<S extends object = any, Args = void, Deps extends object = Record<string, never>>

Cubit<S> is a StateContainer<S> with emit / patch exposed as public mutation surface. Today it adds nothing structurally beyond StateContainer — both are inherited from the underlying StructuralContainer<S>. Kept as a real class (not a type alias) because downstream code does instance instanceof Cubit checks.

The class body is intentionally empty: a no-op emit override would still go through applyState, and patch is inherited from StructuralContainer (path-diffed, microtask-flushed). A caller that wants "skip if no real change" patch semantics can wrap patch themselves or call emit after a manual equality check.

Cubit
} from '@blac/core';
import type {
type ExtractState<T> = T extends StateContainerConstructor<infer S extends object> ? Readonly<S> : never

Extract the state type from a StateContainer

@templateT - The StateContainer type

ExtractState
,
type ExtractArgs<T> = T extends new () => StateContainer<any, infer A, any> ? A : void

Extract the args type (serializable construction/identity data) from a StateContainer subclass.

@templateT - The StateContainer constructor type

ExtractArgs
} from '@blac/core';
class
class UserCubit
UserCubit
extends
class Cubit<S extends object = any, Args = void, Deps extends object = Record<string, never>>

Cubit<S> is a StateContainer<S> with emit / patch exposed as public mutation surface. Today it adds nothing structurally beyond StateContainer — both are inherited from the underlying StructuralContainer<S>. Kept as a real class (not a type alias) because downstream code does instance instanceof Cubit checks.

The class body is intentionally empty: a no-op emit override would still go through applyState, and patch is inherited from StructuralContainer (path-diffed, microtask-flushed). A caller that wants "skip if no real change" patch semantics can wrap patch themselves or call emit after a manual equality check.

Cubit
<{
name: string
name
: string }, {
userId: string
userId
: string }> {
constructor() {
super({
name: string
name
: '' });
}
}
type
type S = {
readonly name: string;
}
S
=
type ExtractState<T> = T extends StateContainerConstructor<infer S extends object> ? Readonly<S> : never

Extract the state type from a StateContainer

@templateT - The StateContainer type

ExtractState
<typeof
class UserCubit
UserCubit
>; // Readonly<{ name: string }>
type
type A = {
userId: string;
}
A
=
type ExtractArgs<T> = T extends new () => StateContainer<any, infer A, any> ? A : void

Extract the args type (serializable construction/identity data) from a StateContainer subclass.

@templateT - The StateContainer constructor type

ExtractArgs
<typeof
class UserCubit
UserCubit
>; // { userId: string }
TypeSignatureWhat it’s forWhere you’d use it
StateContainerConstructor<S>new (...args: any[]) => StateContainer<S, any, any>Minimal constructor constraint, parameterized by state. No static registry methods.A helper that only needs “some container class”.
BlocInstanceType<T>T extends abstract new (...args: any) => infer R ? R : anyAbstract-aware sibling of TS’s built-in InstanceType<T> (which rejects abstract constructors like Cubit/StateContainer).”The instance type of this class” when the class is abstract.
BlocConstructor<S, T>(new (...args: any[]) => InstanceType<T>) & { keepAlive?: boolean }A constructor type with an optional keepAlive static flag. acquire/borrow/ensure/release are standalone functions, not static methods — this type carries no registry surface. Note: the keepAlive field looks unused in practice — isKeepAliveClass reads the class’s static prop directly, not this type field.Rare; prefer StateContainerConstructor unless you specifically need the keepAlive field.
InstanceReadonlyState<T>Omit<InstanceType<T>, 'state'> & { state: ExtractState<T> }The full instance (methods, getters) with state narrowed to Readonly.The bloc element of useBloc’s return tuple.
InstanceState<T>Same, with mutable state: ExtractStateMutable<T>Same, but writable state.Rare; only when you need a mutable view on a typed instance.
StateContainerInstance<S>Omit<StateContainer<S, any, any>, 'state'> & { state: Readonly<S> }”Any container holding this state shape,” keyed by S rather than a concrete class.Function params that accept any container of a known state shape (e.g. releaseInstance).
import {
class Cubit<S extends object = any, Args = void, Deps extends object = Record<string, never>>

Cubit<S> is a StateContainer<S> with emit / patch exposed as public mutation surface. Today it adds nothing structurally beyond StateContainer — both are inherited from the underlying StructuralContainer<S>. Kept as a real class (not a type alias) because downstream code does instance instanceof Cubit checks.

The class body is intentionally empty: a no-op emit override would still go through applyState, and patch is inherited from StructuralContainer (path-diffed, microtask-flushed). A caller that wants "skip if no real change" patch semantics can wrap patch themselves or call emit after a manual equality check.

Cubit
} from '@blac/core';
import type {
type InstanceReadonlyState<T extends StateContainerConstructor = any> = Omit<InstanceType<T>, "state"> & {
state: ExtractState<T>;
}
InstanceReadonlyState
,
type StateContainerConstructor<S extends object = any> = new (...args: any[]) => StateContainer<S, any, any>

Constructor type for StateContainer classes

@templateS - State type managed by the container

StateContainerConstructor
} from '@blac/core';
class
class CounterCubit
CounterCubit
extends
class Cubit<S extends object = any, Args = void, Deps extends object = Record<string, never>>

Cubit<S> is a StateContainer<S> with emit / patch exposed as public mutation surface. Today it adds nothing structurally beyond StateContainer — both are inherited from the underlying StructuralContainer<S>. Kept as a real class (not a type alias) because downstream code does instance instanceof Cubit checks.

The class body is intentionally empty: a no-op emit override would still go through applyState, and patch is inherited from StructuralContainer (path-diffed, microtask-flushed). A caller that wants "skip if no real change" patch semantics can wrap patch themselves or call emit after a manual equality check.

Cubit
<{
count: number
count
: number }> {
constructor() {
super({
count: number
count
: 0 });
}
}
function
function describe(Bloc: StateContainerConstructor): void
describe
(
type Bloc: StateContainerConstructor
Bloc
:
type StateContainerConstructor<S extends object = any> = new (...args: any[]) => StateContainer<S, any, any>

Constructor type for StateContainer classes

@templateS - State type managed by the container

StateContainerConstructor
) {} // "some container class"
declare const
const c: InstanceReadonlyState<typeof CounterCubit>
c
:
type InstanceReadonlyState<T extends StateContainerConstructor = any> = Omit<InstanceType<T>, "state"> & {
state: ExtractState<T>;
}
InstanceReadonlyState
<typeof
class CounterCubit
CounterCubit
>;
const c: InstanceReadonlyState<typeof CounterCubit>
c
.
state: Readonly<{
count: number;
}>
state
.
count: number
count
; // Readonly<{ count: number }>

BlaC tags instance-identity strings with a compile-time-only brand so a plain string can’t be passed where a specific branded ID is expected. No runtime footprint — branded values are just strings at runtime.

ExportSignatureWhat it’s forWhere you’d use it
Brand<T, B>T & { [brand]: B }General nominal-typing helper — two Brands with different B are incompatible even when T is identical.Rolling your own branded primitive type.
BrandedId<B>Brand<string, B>Convenience alias for branding a string.Any string-ID type you want to brand.
InstanceIdBrand<string, 'InstanceId'>The branded type BlaC’s registry/identity APIs accept and return instead of a bare string.Typing a parameter that must be an instance ID, not an arbitrary string.
instanceId(id)(id: string) => InstanceIdValue-level helper that brands a plain string — a pure runtime cast (returns the input unchanged).Producing an InstanceId without writing as InstanceId yourself.
import {
function instanceId(id: string): InstanceId

Create a branded InstanceId from a string

@paramid - The string ID to brand

@returnsBranded InstanceId

instanceId
} from '@blac/core';
import type {
type InstanceId = string & {
[brand]: "InstanceId";
}

Branded string type for state container instance IDs

InstanceId
} from '@blac/core';
declare function
function lookup(id: InstanceId): void
lookup
(
id: InstanceId
id
:
type InstanceId = string & {
[brand]: "InstanceId";
}

Branded string type for state container instance IDs

InstanceId
): void;
function lookup(id: InstanceId): void
lookup
(
function instanceId(id: string): InstanceId

Create a branded InstanceId from a string

@paramid - The string ID to brand

@returnsBranded InstanceId

instanceId
('user-42')); // ok
function lookup(id: InstanceId): void
lookup
('user-42'); // plain string rejected
Error ts(2345) ― Argument of type 'string' is not assignable to parameter of type 'InstanceId'. Type 'string' is not assignable to type '{ [brand]: "InstanceId"; }'.
  • TypeScript — typing blocs end to end: generics, inference, pitfalls
  • Cubit — the class these utilities extract types from
  • Instance Managementacquire/borrow/release, the functions BlocConstructor/StateContainerConstructor type
  • Passing Inputs — the args/deps identity model ExtractArgs/ExtractDeps read