Skip to main content

typectx v0.8: A ProductSupplier Type You Can Actually Use

· 4 min read
@someone635

Version 0.8 rewrites typectx's internals so that the ProductSupplier type is finally practical for explicit type annotations. If you've ever wanted to annotate your suppliers to speed up TypeScript's language server in a large codebase, v0.7's generic signatures made that painful. v0.8 fixes the foundation.

Let's dive in.

The Problem: ProductSupplier Was Unusable for Annotations

In v0.7.1, ProductSupplier carried a giant generic signature inherited from the monolithic implementation. The defaults were wrong, the internal metadata was nested under a _ namespace, and the type parameters didn't have sensible enough defaults for you to write a simple annotation without specifying everything.

This matters because in large codebases, TypeScript's language server spends significant time re-inferring supplier types across files. The standard fix is explicit annotations—but if the annotation type itself is hostile, nobody will use it.

// v0.7.1 - The old ProductSupplier had too many required parameters
// and nested internal types that leaked into the signature
type ProductSupplier<
NAME, CONSTRAINT, SUPPLIERS, OPTIONALS, ASSEMBLERS,
HIRED, TEAM, DEPS, RESOLVED, CTX
> = { ... }

// Trying to annotate? Good luck.
const $myService: ProductSupplier<...ten params...> = market.add("myService").product({ ... })

The Fix: Cleaner Generics, Better Defaults

v0.8 restructures the type model around three clearer primitives:

  • BaseSupplier — shared shape for all suppliers
  • RequestSupplier — for request-scoped data
  • ProductSupplier — for factory-produced values

ProductSupplier now has sensible defaults for most parameters:

// v0.8 - ProductSupplier with defaults
interface ProductSupplier<
NAME extends string,
CONSTRAINT,
SUPPLIERS extends MainSupplier[] = [],
OPTIONALS extends RequestSupplier[] = [],
ASSEMBLERS extends UnknownProductSupplier[] = [],
HIRED extends UnknownProductSupplier[] = [],
KNOWN extends Record<string, unknown> = Record<never, unknown>,
MOCK extends boolean = boolean,
COMPOSITE extends boolean = boolean
> { ... }

Most of the time, you only need to specify NAME, CONSTRAINT, SUPPLIERS, OPTIONALS and ASSEMBLERS, which you can just copy-paste from your implementation. All the other params are inferred from those 5.

Internal Shape Cleanup: _ Namespace Removed

v0.7.1 tucked internal metadata under a _ namespace:

// v0.7.1
supplier._.product // true
supplier._.mock // false
supply._.ctx // the ctx function
supply._.packed // boolean

v0.8 flattens these to top-level fields:

// v0.8
supplier._product // true
supplier._request // false
supplier._mock // false
supply._ctx // the ctx function
supply._packed // boolean

This is a breaking change if you touched internals directly, but it removes an entire layer of indirection from both the runtime and the type system.

v0.7.1v0.8
supplier._.productsupplier._product
supplier._.mocksupplier._mock
supply._.ctxsupply._ctx
supply._.packedsupply._packed

Modular Runtime

To make all of this maintainable, the monolithic implementation from v0.7.1 was split into focused modules:

ModulePurpose
src/product/main.tsCore supplier construction and team building
src/product/assemble.tsThe assemble() method
src/product/build.tsInternal _build(), Ctx(), reassemble()
src/product/hire.tsThe hire() method
src/product/mock.tsThe mock() method
src/request.tsRequest supplier factory

The public API (market.add().request() / .product()) is unchanged. This is purely an internal restructure.

Migration Guide

Step 1: Update internal field access (if applicable)

// Before
supplier._.product
supply._.ctx

// After
supplier._product
supply._ctx

Step 2: Update type imports (if using them)

// Before - v0.7.1 types
import type { Supply, ProductSupplier, Ctx, Deps, Resolved } from "typectx"
// These still exist, but their generic signatures have changed.

// After - v0.8 types (same names, cleaner generics)
import type { Supply, ProductSupplier, Ctx, Deps, Resolved } from "typectx"

Why These Changes?

  1. Practical annotations: ProductSupplier is now usable as an explicit type, which is the single biggest lever for TS server performance in large codebases
  2. Cleaner internals: Flattened _ namespace reduces type complexity and runtime indirection
  3. Maintainable core: Modular runtime makes future changes safer

These are breaking changes for anyone touching internals or types directly, but for most app-level code, the migration is minimal.


npm install typectx@latest @typectx/react@latest