-
Notifications
You must be signed in to change notification settings - Fork 717
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
"TypeScript's constructor assignments" is not working #1555
Comments
Hey, I have the same problem did you found an anwser ? |
Nope. I'm using |
Have the same issue, filed another issue |
Hey @mizozobu, @cloudhx , I just recreated it with a test and worked like a charm: import { expect } from "chai";
import { Container, injectable } from "../../src/inversify";
describe("Issue 1555", () => {
it("should instantiate object with injected constructor parameters", () => {
@injectable()
class Katana {
public hit() {
return "cut!";
}
}
@injectable()
class Shuriken {
public throw() {
return "hit!";
}
}
@injectable()
class Ninja {
public constructor(public katana: Katana, public shuriken: Shuriken) {}
public fight() {
return this.katana.hit();
}
public sneak() {
return this.shuriken.throw();
}
}
const container = new Container();
container.bind<Ninja>(Ninja).to(Ninja);
container.bind<Katana>(Katana).to(Katana);
container.bind<Shuriken>(Shuriken).to(Shuriken);
const ninja = container.get<Ninja>(Ninja);
expect(ninja.katana).to.deep.eq(new Katana());
expect(ninja.shuriken).to.deep.eq(new Shuriken());
});
}); Please try to recreate it in codesandbox or in any other platform in which we can see the issue. We cannot identify nor solve the issue without recreating it |
I tested with the environment provided, library version, os and nodejs version, and everything works just fine. Would be nice to have your Here are the steps: MINIMUM TSCONFIG (you can use any supported target, I am just using the latest one) {
"compilerOptions": {
"target": "ES2023",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"strictPropertyInitialization": false
}
} MUST HAVE DEPENDENCY npm i reflect-metadata Obs: Reflect metadata has to be the first thing to be initialized on top of your project CODE import "reflect-metadata";
import { Container, injectable } from "inversify";
@injectable()
class Dagger {
public throw() {
console.log("throw dagger");
}
}
@injectable()
class Ninja {
public constructor(private _dagger: Dagger) {}
public throwDagger() {
this._dagger.throw();
}
}
const container = new Container();
container.bind<Ninja>(Ninja).toSelf();
container.bind<Dagger>(Dagger).toSelf();
container.get(Ninja).throwDagger(); Should work just fine. Please, let us know if the issue has been resolved based on the configuration I gave you, so that we can close this topic. OUTPUT |
When I created a brand new project with @rsaz's code and compiled it with Since it just worked fine on its own, I'm guessing it's an issue with the remix compiler or just how I set it up. Here's the minimium repro. What am I missing? |
Hi @mizozobu The issue here is architectural. Let me walk you through the situation: 1 - In your code below, you're trying to inject a class, in this case @injectable()
class Ninja {
public constructor(private _dagger:Dagger) {}
public throwDagger() {
this._dagger.throw();
}
}
@injectable()
class Dagger {
public throw() {
console.log('throw dagger');
}
}
const container = new Container();
container.bind<Ninja>(Ninja).toSelf();
container.bind<Dagger>(Dagger).toSelf();
container.get(Ninja).throwDagger(); Change the order to: @injectable()
class Dagger {
public throw() {
console.log("throw dagger");
}
}
@injectable()
class Ninja {
public constructor(@inject(Dagger) private _dagger: Dagger) {}
public throwDagger() {
this._dagger.throw();
}
}
const container = new Container();
container.bind<Ninja>(Ninja).toSelf();
container.bind<Dagger>(Dagger).toSelf();
container.get(Ninja).throwDagger(); Now related to the issue referred in this ticket: The reason why you're getting the issue above is just the nature of Typescript works. As we all know, Typescript doesn't have a good reflection system, so Inversify relies heavily on When your declaration of classes are straightforward, injected outside of any extra context (in this case Inversify resolves this issue forcing the use of the 1 - using the FINAL CODE import "reflect-metadata";
import { Container, inject, injectable } from "inversify";
/**
* By default, Remix will handle generating the HTTP Response for you.
* You are free to delete this file if you'd like to, but if you ever want it revealed again, you can run `npx remix reveal` ✨
* For more information, see https://remix.run/file-conventions/entry.server
*/
import { PassThrough } from "node:stream";
import type { AppLoadContext, EntryContext } from "@remix-run/node";
import { createReadableStreamFromReadable } from "@remix-run/node";
import { RemixServer } from "@remix-run/react";
import { isbot } from "isbot";
import { renderToPipeableStream } from "react-dom/server";
const ABORT_DELAY = 5_000;
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext,
// This is ignored so we can keep it in the template for visibility. Feel
// free to delete this parameter in your app if you're not using it!
// eslint-disable-next-line @typescript-eslint/no-unused-vars
loadContext: AppLoadContext
) {
@injectable()
class Dagger {
public throw() {
console.log("throw dagger");
}
}
@injectable()
class Ninja {
public constructor(@inject(Dagger) private _dagger: Dagger) {}
public throwDagger() {
this._dagger.throw();
}
}
const container = new Container();
container.bind<Ninja>(Ninja).toSelf();
container.bind<Dagger>(Dagger).toSelf();
container.get(Ninja).throwDagger();
return isbot(request.headers.get("user-agent") || "")
? handleBotRequest(request, responseStatusCode, responseHeaders, remixContext)
: handleBrowserRequest(request, responseStatusCode, responseHeaders, remixContext);
}
function handleBotRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />,
{
onAllReady() {
shellRendered = true;
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);
responseHeaders.set("Content-Type", "text/html");
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
})
);
pipe(body);
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
// Log streaming rendering errors from inside the shell. Don't log
// errors encountered during initial shell rendering since they'll
// reject and get logged in handleDocumentRequest.
if (shellRendered) {
console.error(error);
}
},
}
);
setTimeout(abort, ABORT_DELAY);
});
}
function handleBrowserRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
return new Promise((resolve, reject) => {
let shellRendered = false;
const { pipe, abort } = renderToPipeableStream(
<RemixServer context={remixContext} url={request.url} abortDelay={ABORT_DELAY} />,
{
onShellReady() {
shellRendered = true;
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);
responseHeaders.set("Content-Type", "text/html");
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
})
);
pipe(body);
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
// Log streaming rendering errors from inside the shell. Don't log
// errors encountered during initial shell rendering since they'll
// reject and get logged in handleDocumentRequest.
if (shellRendered) {
console.error(error);
}
},
}
);
setTimeout(abort, ABORT_DELAY);
});
} |
This issue was moved to a discussion.
You can continue the conversation there. Go to discussion →
"TypeScript's constructor assignments" as stated here is not working.
Expected Behavior
No errors are thrown.
Current Behavior
Throws
Error: Missing required @inject or @multiInject annotation in: argument 0 in class Ninja.
Possible Solution
With
@inject
decorator, everything works perfectly.Steps to Reproduce (for bugs)
Context
N/A
Your Environment
Stack trace
The text was updated successfully, but these errors were encountered: