Skip to content

Support type inference - #408

Merged
jeffijoe merged 1 commit into
jeffijoe:masterfrom
kibertoad:feat/type-inference
Feb 14, 2026
Merged

Support type inference#408
jeffijoe merged 1 commit into
jeffijoe:masterfrom
kibertoad:feat/type-inference

Conversation

@kibertoad

@kibertoad kibertoad commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

This PR offers a way to decrease amount of boilerplate involved in typing dependencies available in the project for the injection.

It would be awesome to provide one more option, that could even replace createContainerWith entirely. However, it would be a breaking change typing-wise.

Here is the proposal:

Typed register() That Returns a Narrowed Container

Make register() return a new container type that includes the registered types:

  register<R extends Record<string, Resolver<any>>>(
    pair: R
  ): AwilixContainer<Cradle & InferCradle<R>>

Usage:

  const container = createContainer()
    .register({
      userService: asClass(UserService),
      logger: asValue(new Logger()),
    })

  // container.cradle.userService => UserService  ✓

Pros: Works with the existing API, supports incremental registration (chain multiple .register() calls and the type
accumulates). Most ergonomic for the chaining pattern.
Cons: This is a breaking change - current register() returns this (same container type), changing it to return a wider
type changes the contract. Also, the runtime object is the same container (mutated), but the type would appear to be
a new container. Users assigning to a pre-typed variable would get errors. Could be mitigated by making it additive
(Cradle & InferCradle<R>), but when Cradle = any, the intersection collapses to any.

The any default problem is significant: any & { userService: UserService } = any. So this only works if users start
with createContainer<{}>() instead of the current createContainer() (which defaults Cradle to any).

@kibertoad

Copy link
Copy Markdown
Contributor Author

@jeffijoe Maybe you would consider removing the lockfile? It is bringing pain and misery on every PR :-/

@jeffijoe

Copy link
Copy Markdown
Owner

Its weird because all I did was npm install locally, why does it keep breaking?

@kibertoad

Copy link
Copy Markdown
Contributor Author

@jeffijoe you need to use older node version, like 20.20.0. newer ones bundle npm that generate format incompatible with older npm.

@kibertoad

Copy link
Copy Markdown
Contributor Author

@jeffijoe Any thoughts on the feature itself?

@jeffijoe

Copy link
Copy Markdown
Owner

To be honest, I don't like that we're optimizing for a typed cradle.

One of the core principles of Awilix is that it should be transparent to your application code. Even if using Proxy mode, you should be defining a MyServiceOptions type that has just the things that you need to construct that service, and shouldn't rely on Awilix at all, even indirectly by having some type MyServiceOptions = Pick<MyGodObjectCradle, 'dep1 | 'dep2'>

With that said, since we already have partial support for it, then I would rather push a new major version by changing the default cradle type from any to what you suggest, rather than introducing a createContainerWith which only would exist because of typings.


Also, please tell your Claude to avoid adding unnecessary tests for stuff that is already covered, and the

// ---------------------------------------------------------------------------

are unnecessary. 😉

I'm perfectly fine with using AI to help write code, but there's a lot of new code added via tests here, and I see a lot that appears redundant (testing runtime functionality that is already covered by existing tests). Then there are tests that (to me) make no sense as to what they are testing?

  it('preserves generic class type parameters', () => {
    class Repository<T> {
      items: T[] = []
      add(item: T) {
        this.items.push(item)
      }
    }

    // asClass can't infer the generic, so use explicit type parameter
    const resolvers = {
      repo: asClass<Repository<string>>(
        Repository as new (...args: any[]) => Repository<string>,
      ),
    }
    type Result = InferCradleFromResolvers<typeof resolvers>

    assertType<IsExact<Result['repo'], Repository<string>>>(true)
  })

Like, what exactly are we testing here? It looks like Claude started writing the test, then realized that TS can't actually infer it, then does Repository as new (...args: any[]) => Repository<string>?

@kibertoad

Copy link
Copy Markdown
Contributor Author

With that said, since we already have partial support for it, then I would rather push a new major version by changing the default cradle type from any to what you suggest, rather than introducing a createContainerWith which only would exist because of typings.

Awesome, I'll rework the PR then!

I see a lot that appears redundant (testing runtime functionality that is already covered by existing tests).

The intent there is not to cover runtime functionality at all, it is focused on type inference, which I don't think existing tests are covering?.. I will revisit them again to check if all the ones that we have make sense and cover valid inference cases.

@kibertoad

Copy link
Copy Markdown
Contributor Author

@jeffijoe Please check now!

Comment thread src/__tests__/type-inference.test.ts Outdated
Comment thread src/__tests__/type-inference.test.ts Outdated
Comment thread src/container.ts
Comment thread src/container.ts Outdated
Comment thread src/container.ts Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated

@jeffijoe jeffijoe left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Please undo those import changes - I have a feeling you were trying to (or your agent was trying to) fix the type import I suggested. You can use a type specifier in the same import block, you don't need a separate import.

Comment thread CHANGELOG.md
Comment thread src/__tests__/function-tokenizer.test.ts Outdated
Comment thread src/__tests__/load-modules.test.ts Outdated
Comment thread src/__tests__/type-inference.test.ts Outdated
@jeffijoe

Copy link
Copy Markdown
Owner

There was just 1 comment left (re: any return type) but I imagine it doesn't matter since its just in the implementation. As usual, could you squash the commits before I merge please?

@jeffijoe
jeffijoe merged commit 1b5c4f2 into jeffijoe:master Feb 14, 2026
6 checks passed
@kibertoad

Copy link
Copy Markdown
Contributor Author

@jeffijoe what do you think about bumping minimum Node version for the next semver major?

@jeffijoe

Copy link
Copy Markdown
Owner

Too late, already published v13. 😅

@kibertoad

Copy link
Copy Markdown
Contributor Author

@jeffijoe it would be a documentation and CI change anyway, so version release shouldn't matter :D

@jeffijoe

Copy link
Copy Markdown
Owner

Fair enough, go for it.

@kibertoad

Copy link
Copy Markdown
Contributor Author

@jeffijoe #411

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