Add inline (token, factory) overloads to provides() and append()#18
Add inline (token, factory) overloads to provides() and append()#18
Conversation
…rovides()
Support provides('token', () => value) and provides('token', ['dep'], (d) => ...)
as alternatives to provides(Injectable('token', ...)), reducing boilerplate for
common service registration patterns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…and Container.append()
Support provides('token', () => value) and provides('token', ['dep'], (d) => ...)
on PartialContainer, and append('token', () => value) / append('token', ['dep'], fn)
on Container, reducing Injectable() boilerplate across more API surfaces.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…o PartialContainer Enable composing PartialContainers by merging in other PartialContainers or Containers. Dependencies are correctly tracked: the other container's services satisfy existing dependencies, and unresolved ones propagate. Add static fromObject() factory for bootstrapping from plain objects. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Lead README and TSDoc examples with the inline provides('token', factory)
and provides('token', deps, factory) forms. Reposition Injectable() as a
lower-level primitive for reusable factory objects and run(). Add examples
to all new PartialContainer overloads (fromObject, provides(Partial),
provides(Container), inline factory forms).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
e94cd2f to
91ed8ac
Compare
msilivonik-sc
left a comment
There was a problem hiding this comment.
Love the change! Much simpler API in some cases!
| keyof Services | ||
| > | ||
| > | ||
| : never; |
There was a problem hiding this comment.
do you think never is a good feedback that we can provide to devs?
const partial = new PartialContainer({})
.provides("Foo", ["bar"] as const, (bar: string, extra: number) => bar)
.provides("Baz", () => "baz"); // Property 'provides' does not exist on type 'never'is there a way to make it better? maybe erroring on the factory function itself when parameter count doesn't match deps?
There was a problem hiding this comment.
Konstantin's AI agent: Fixed. Removed the conditional never return type — the error now only appears on the factory function via the ParamCountMismatch sentinel type (replaces void[] for a more readable compiler message). The next chained call compiles cleanly. Added @ts-expect-error regression tests to verify this stays correct.
There was a problem hiding this comment.
Konstantin's AI agent: Good catch — the branded string intersection (string & { __brand: unique symbol }) caused TS to structurally expand comparisons against complex types like Date, listing all missing properties. Fixed by switching to an interface with a descriptive never property:
export interface ParamCountMismatch {
readonly "Error: factory parameter count must match dependency count": never;
}Now both cases produce the same clean error: Type 'ParamCountMismatch' is not assignable to type 'Date' — no property expansion. And hovering the type name in the IDE still shows the error message.
- Replace duck-typing ("factories" in first) with instanceof Container
- Convert arrow function properties to methods for consistency
- Reduce `any` usage: type impl signature, extract addInjectable helper
- Remove conditional `never` return type — error now only targets factory
- Replace `void[]` with `ParamCountMismatch` sentinel for readable errors
- Add regression tests and error message assertions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
msilivonik-sc
left a comment
There was a problem hiding this comment.
LGTM! Left one question about compiler error message, but if it compilcates things a lot, then I guess you can ignore it.
The branded string intersection (`string & { __brand: unique symbol }`)
caused TS to expand structural comparisons against complex types like Date,
producing verbose "missing properties" errors. Using an interface with a
descriptive `never` property keeps the type name visible in errors without
structural expansion.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Breaking: arrow function properties converted to methods (providesClass, providesValue, appendValue, appendClass). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6ff2250 to
6767674
Compare


Stacked on #17.
Summary
Add inline
(token, factory)and(token, deps, factory)overloads as alternatives to wrapping withInjectable():Container.provides()
provides('token', () => value)— zero-dep lazy factoryprovides('token', ['dep'] as const, (dep) => ...)— factory with type-checked dependenciesContainer.provides('token', () => value)entry pointContainer.append()
append('token', () => value)— zero-dep appendappend('token', ['dep'] as const, (dep) => ...)— append with dependenciesPartialContainer.provides()
(token, fn)and(token, deps, fn)overloads with dependency trackingprovides(PartialContainer)— compose partial containersprovides(Container)— merge resolved services (satisfies existing deps)PartialContainer.fromObject()
Test plan
npm test— 75 tests pass (13 new covering all new overloads + type errors)npm run styleguide— lint + prettier cleannpm run compile— no type errors🤖 Generated with Claude Code