Skip to content

Add inline (token, factory) overloads to provides() and append()#18

Merged
kburov-sc merged 9 commits intomainfrom
kburov/inline-provides-overloads
Mar 11, 2026
Merged

Add inline (token, factory) overloads to provides() and append()#18
kburov-sc merged 9 commits intomainfrom
kburov/inline-provides-overloads

Conversation

@kburov-sc
Copy link
Copy Markdown
Collaborator

Stacked on #17.

Summary

Add inline (token, factory) and (token, deps, factory) overloads as alternatives to wrapping with Injectable():

Container.provides()

  • provides('token', () => value) — zero-dep lazy factory
  • provides('token', ['dep'] as const, (dep) => ...) — factory with type-checked dependencies
  • Static Container.provides('token', () => value) entry point

Container.append()

  • append('token', () => value) — zero-dep append
  • append('token', ['dep'] as const, (dep) => ...) — append with dependencies

PartialContainer.provides()

  • Same (token, fn) and (token, deps, fn) overloads with dependency tracking
  • provides(PartialContainer) — compose partial containers
  • provides(Container) — merge resolved services (satisfies existing deps)

PartialContainer.fromObject()

  • Static factory to bootstrap from a plain object

Test plan

  • npm test — 75 tests pass (13 new covering all new overloads + type errors)
  • npm run styleguide — lint + prettier clean
  • npm run compile — no type errors

🤖 Generated with Claude Code

@kburov-sc kburov-sc requested a review from msilivonik-sc March 5, 2026 10:03
Base automatically changed from kburov/docs-streamlined-apis to main March 6, 2026 01:45
kburov-sc and others added 4 commits March 6, 2026 12:46
…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>
@kburov-sc kburov-sc force-pushed the kburov/inline-provides-overloads branch from e94cd2f to 91ed8ac Compare March 6, 2026 01:47
Copy link
Copy Markdown
Collaborator

@msilivonik-sc msilivonik-sc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love the change! Much simpler API in some cases!

Comment thread src/PartialContainer.ts Outdated
Comment thread src/Container.ts
Comment thread src/PartialContainer.ts Outdated
Comment thread src/PartialContainer.ts Outdated
keyof Services
>
>
: never;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see you added new ParamCountMismatch with some message, does this message expected to be part of compiler output? Check these examples:

  1. this one is good:
Screenshot 2026-03-09 at 6 58 18 PM 2. this one is still cryptic: Screenshot 2026-03-09 at 6 58 01 PM

It is def better then never, but maybe there is a way to improve that?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

kburov-sc and others added 2 commits March 8, 2026 14:09
- 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
msilivonik-sc previously approved these changes Mar 9, 2026
Copy link
Copy Markdown
Collaborator

@msilivonik-sc msilivonik-sc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
msilivonik-sc
msilivonik-sc previously approved these changes Mar 10, 2026
Copy link
Copy Markdown
Collaborator

@msilivonik-sc msilivonik-sc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Breaking: arrow function properties converted to methods (providesClass,
providesValue, appendValue, appendClass).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kburov-sc kburov-sc force-pushed the kburov/inline-provides-overloads branch from 6ff2250 to 6767674 Compare March 11, 2026 00:11
Copy link
Copy Markdown
Collaborator

@msilivonik-sc msilivonik-sc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@kburov-sc kburov-sc merged commit 1972233 into main Mar 11, 2026
1 check passed
@kburov-sc kburov-sc deleted the kburov/inline-provides-overloads branch March 11, 2026 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants