Skip to content

Types

The complete type toolkit exported from @blac/core. These utilities let you derive the state, args, deps, and instance shapes of a container class without hand-writing them, and the branded-ID helpers give you nominal InstanceId strings. Every signature on this page is quoted verbatim from source.

For a task-oriented walkthrough of typing blocs (generic parameters, inference, common pitfalls) see TypeScript. This page is the reference: one heading per export, the exact signature, what it does, and a small example.

All examples assume this setup, which is hidden from the rendered snippet but type-checked:

import {
class Cubit<S extends object = any, Args = void, Deps extends object = Record<string, never>>

Cubit<S> is a StateContainer<S> that exposes mutation publicly.

That is the whole difference, and it is a real one: on StateContainer emit / patch / update are protected, so a container mutates itself from its own methods and callers go through the API it chooses to publish. Cubit re-declares the three as public for the cases where a caller legitimately drives state from outside — test helpers, devtools time-travel, benchmarks.

Reach for StateContainer when business logic should live in the class, and Cubit when the caller owns the transitions.

Cubit
} from '@blac/core';
interface
interface CounterState
CounterState
{
CounterState.count: number
count
: number;
CounterState.label: string
label
: string;
}
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> that exposes mutation publicly.

That is the whole difference, and it is a real one: on StateContainer emit / patch / update are protected, so a container mutates itself from its own methods and callers go through the API it chooses to publish. Cubit re-declares the three as public for the cases where a caller legitimately drives state from outside — test helpers, devtools time-travel, benchmarks.

Reach for StateContainer when business logic should live in the class, and Cubit when the caller owns the transitions.

Cubit
<
interface CounterState
CounterState
> {
constructor() {
super({
CounterState.count: number
count
: 0,
CounterState.label: string
label
: 'idle' });
}
}

A StateContainer (and therefore Cubit) carries three type parameters: S (state), Args (serializable construction/identity data), and Deps (injected non-serializable handles). These utilities pull each one back out of a container class (the constructor, e.g. typeof CounterCubit), so you never have to re-declare a state shape that already lives on the class.

Signature

export type ExtractState<T> =
T extends StateContainerConstructor<infer S> ? Readonly<S> : never;

Extracts the state type from a container constructor as a Readonly<S>. This is the type you get back from useBloc and from the state getter — read-only, because state is immutable from the outside. Resolves to never if T is not a container constructor.

type S =
type ExtractState<T> = T extends StateContainerConstructor<infer S extends object> ? DeepReadonly<S> : never

Extract the state type from a StateContainer, deeply readonly.

State is owned by the container: consumers read it and mutate only through emit/patch, so nested writes are a compile error rather than a silent desync from the change-tracking channel.

@typeParam ― T - The StateContainer type

ExtractState
<typeof
class CounterCubit
CounterCubit
>;
type S = {
readonly count: number;
readonly label: string;
}

Signature

export type ExtractStateMutable<T> =
T extends StateContainerConstructor<infer S> ? S : never;

The same as ExtractState, but without the Readonly<> wrapper — the raw S as the class declared it. Reach for this only when you genuinely need a mutable view (for example, building the next state object before you emit it); prefer ExtractState everywhere a value is being read.

type Draft =
type ExtractStateMutable<T> = T extends StateContainerConstructor<infer S extends object> ? S : never
ExtractStateMutable
<typeof
class CounterCubit
CounterCubit
>;
type Draft = CounterState

Signature

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

Extracts the Args type — the serializable data a bloc is constructed/identified with (see Passing Inputs). Falls back to void when the class declares no args. Note the match is against a zero-argument constructor shape (new () =>), which is how container subclasses are written.

interface
interface UserState
UserState
{
UserState.name: string
name
: string;
}
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> that exposes mutation publicly.

That is the whole difference, and it is a real one: on StateContainer emit / patch / update are protected, so a container mutates itself from its own methods and callers go through the API it chooses to publish. Cubit re-declares the three as public for the cases where a caller legitimately drives state from outside — test helpers, devtools time-travel, benchmarks.

Reach for StateContainer when business logic should live in the class, and Cubit when the caller owns the transitions.

Cubit
<
interface UserState
UserState
, {
userId: string
userId
: string }> {
constructor() {
super({
UserState.name: string
name
: '' });
}
}
type 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.

@typeParam ― T - The StateContainer constructor type

ExtractArgs
<typeof
class UserCubit
UserCubit
>;
type A = {
userId: string;
}

Signature

export type ExtractDeps<T> = T extends new () => StateContainer<
any,
any,
infer D
>
? D
: Record<string, never>;

Extracts the Deps type — the injected non-serializable handles (clients, services, other blocs) a container depends on. Falls back to Record<string, never> (the “no deps” shape) when none are declared.

interface
interface FeedState
FeedState
{
FeedState.posts: string[]
posts
: string[];
}
class
class FeedCubit
FeedCubit
extends
class Cubit<S extends object = any, Args = void, Deps extends object = Record<string, never>>

Cubit<S> is a StateContainer<S> that exposes mutation publicly.

That is the whole difference, and it is a real one: on StateContainer emit / patch / update are protected, so a container mutates itself from its own methods and callers go through the API it chooses to publish. Cubit re-declares the three as public for the cases where a caller legitimately drives state from outside — test helpers, devtools time-travel, benchmarks.

Reach for StateContainer when business logic should live in the class, and Cubit when the caller owns the transitions.

Cubit
<
interface FeedState
FeedState
, void, {
api: {
fetch(): void;
}
api
: {
function fetch(): void
fetch
(): void } }> {
constructor() {
super({
FeedState.posts: string[]
posts
: [] });
}
}
type D =
type ExtractDeps<T> = T extends new () => StateContainer<any, any, infer D> ? D : Record<string, never>

Extract the deps type (injected non-serializable handles) from a StateContainer subclass.

@typeParam ― T - The StateContainer constructor type

ExtractDeps
<typeof
class FeedCubit
FeedCubit
>;
type D = {
api: {
fetch(): void;
};
}

These describe the instance a container class produces, and the constructor shape that @blac/core and @blac/react use to type their entry points.

Signature

export type InstanceReadonlyState<T extends StateContainerConstructor = any> =
Omit<InstanceType<T>, 'state'> & { state: ExtractState<T> };

The instance type of a container class with its state property narrowed to the Readonly state for that class. Useful when you want the full instance (methods, getters) but with a precisely-typed, read-only state.

type
type ReadonlyInstance = CounterCubit & {
state: {
readonly count: number;
readonly label: string;
};
}
ReadonlyInstance
=
type InstanceReadonlyState<T extends StateContainerConstructor = StateContainerConstructor<any>> = InstanceType<T> & {
state: ExtractState<T>;
}
InstanceReadonlyState
<typeof
class CounterCubit
CounterCubit
>;
declare const
const c: ReadonlyInstance
c
:
type ReadonlyInstance = CounterCubit & {
state: {
readonly count: number;
readonly label: string;
};
}
ReadonlyInstance
;
const c: ReadonlyInstance
c
.
state: CounterState & {
readonly count: number;
readonly label: string;
}
state
.count;
CounterState.count: number

Signature

export type InstanceState<T extends StateContainerConstructor = any> = Omit<
InstanceType<T>,
'state'
> & { state: ExtractStateMutable<T> };

Like InstanceReadonlyState, but the state property is the mutable S rather than Readonly<S>. Reach for it only when you specifically need a writable view of state on a typed instance.

type
type MutableInstance = CounterCubit & {
state: CounterState;
}
MutableInstance
=
type InstanceState<T extends StateContainerConstructor = StateContainerConstructor<any>> = InstanceType<T> & {
state: ExtractStateMutable<T>;
}
InstanceState
<typeof
class CounterCubit
CounterCubit
>;

Signature

export type StateContainerInstance<S extends object = any> = Omit<
StateContainer<S, any, any>,
'state'
> & { state: Readonly<S> };

A StateContainer instance keyed by its state type S rather than by a concrete class, with state narrowed to Readonly<S>. Use it when you want to describe “any container holding this state shape” without pinning a specific subclass.

interface
interface CounterState
CounterState
{
CounterState.count: number
count
: number;
CounterState.label: string
label
: string;
}
function
function readCount(c: StateContainerInstance<CounterState>): number
readCount
(
c: StateContainerInstance<CounterState>
c
:
type StateContainerInstance<S extends object = any> = StateContainer<S, any, any> & {
state: Readonly<S>;
}
StateContainerInstance
<
interface CounterState
CounterState
>) {
return
c: StateContainerInstance<CounterState>
c
.
state: CounterState & Readonly<CounterState>
state
.
CounterState.count: number
count
;
}

Signature

export type StateContainerConstructor<S extends object = any> = new (
...args: any[]
) => StateContainer<S, any, any>;

The minimal constructor type for a container class, parameterized by state S. It is the constraint the extraction utilities (ExtractState, ExtractStateMutable, InstanceReadonlyState, InstanceState) match against. Use it where you only care that something is a container class.

const
const Ctor: StateContainerConstructor<CounterState>
Ctor
:
type StateContainerConstructor<S extends object = any> = new () => StateContainer<S, any, any>

Constructor type for StateContainer classes

@typeParam ― S - State type managed by the container

StateContainerConstructor
<
interface CounterState
CounterState
> =
class CounterCubit
CounterCubit
;

BlaC tags instance-identity strings with a brand so a plain string cannot be passed where an InstanceId is expected. The brand is a compile-time-only phantom property keyed by a unique symbol; it has no runtime footprint — branded values are just strings at runtime.

Signature

declare const brand: unique symbol;
export type Brand<T, B> = T & { [brand]: B };

The general nominal-typing helper: intersects a base type T with a phantom property under a private unique symbol, tagged by the brand identifier B. Two Brands with different B are incompatible even when T is identical, which is what prevents accidental mixing of similar primitive types.

type
type UserId = string & {
[brand]: "UserId";
}
UserId
=
type Brand<T, B> = T & {
[brand]: B;
}

Nominal type helper, preventing accidental confusion between similar primitive types.

@typeParam ― T - The base type

@typeParam ― B - The brand identifier

Brand
<string, 'UserId'>;
type
type OrderId = string & {
[brand]: "OrderId";
}
OrderId
=
type Brand<T, B> = T & {
[brand]: B;
}

Nominal type helper, preventing accidental confusion between similar primitive types.

@typeParam ― T - The base type

@typeParam ― B - The brand identifier

Brand
<string, 'OrderId'>;
declare const
const u: UserId
u
:
type UserId = string & {
[brand]: "UserId";
}
UserId
;
declare function
function loadOrder(id: OrderId): void
loadOrder
(
id: OrderId
id
:
type OrderId = string & {
[brand]: "OrderId";
}
OrderId
): void;
function loadOrder(id: OrderId): void
loadOrder
(u);
Error ts(2345) ― Argument of type 'UserId' is not assignable to parameter of type 'OrderId'. Type 'UserId' is not assignable to type '{ [brand]: "OrderId"; }'. Types of property '[brand]' are incompatible. Type '"UserId"' is not assignable to type '"OrderId"'.

Signature

export type BrandedId<B> = Brand<string, B>;

A convenience alias for the common case of branding a string: BrandedId<B> is exactly Brand<string, B>. Use it whenever the base type is a string ID.

type
type SessionId = string & {
[brand]: "SessionId";
}
SessionId
=
type BrandedId<B> = string & {
[brand]: B;
}

Branded string type for type-safe IDs.

@typeParam ― B - The brand identifier

BrandedId
<'SessionId'>;

Signature

export type InstanceId = Brand<string, 'InstanceId'>;

The branded string type BlaC uses for state-container instance identities — BrandedId<'InstanceId'> by another name. Registry and identity APIs accept and return this rather than a bare string, so an arbitrary string cannot stand in for an instance key by accident.

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
('not-branded');
Error ts(2345) ― Argument of type 'string' is not assignable to parameter of type 'InstanceId'. Type 'string' is not assignable to type '{ [brand]: "InstanceId"; }'.

Signature

export function instanceId(id: string): InstanceId {
return id as InstanceId;
}

The value-level helper that brands a plain string as an InstanceId. It is a pure cast at runtime (returns the input unchanged); its only job is to give you a typed InstanceId to hand to APIs that require one, without writing the as InstanceId assertion yourself.

const
const id: InstanceId
id
:
type InstanceId = string & {
[brand]: "InstanceId";
}

Branded string type for state container instance IDs.

InstanceId
=
function instanceId(id: string): InstanceId
instanceId
('user-42');
  • TypeScript — typing blocs end to end: generics, inference, and pitfalls
  • Cubit — the class these utilities extract types from
  • Instance Management — acquire / borrow / release and the registry surface
  • Passing Inputs — the args / deps identity model that ExtractArgs and ExtractDeps read
  • useBloc — where ExtractState shows up as the hook’s return type