You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
and suppose we want to bring a bit of reflection in our code:
index.ts
import'reflect-metadata';import{getDesignType}from'./utils';importClassOnefrom'./classOne';importClassTwofrom'./classTwo';console.log(`Hello World! ${ClassOne.name} and ${ClassTwo.name};)`);console.log(`--`);console.log(`${ClassOne.name} property is of type ${getDesignType(ClassOne,'property')}`);console.log(`${ClassTwo.name} property is of type ${getDesignType(ClassTwo,'property')}`);
utils.ts
/** * Use typescript emited metadata to get the type * * @param ctor * @param key */exportconstgetDesignType=(ctor: Function,key: string)=>{consttype=Reflect.getMetadata('design:type',ctor.prototype,key);returntype?.name;}/** * Dummy decorator * * @param target * @param key */exportconstproperty=(target: any,key: string)=>{}
Expected behavior:
foo@bar:~$ node ./distHello World! ClassOne and ClassTwo;)--ClassOne property is of type ClassTwoClassTwo property is of type ClassOne
Actual behavior:
foo@bar:~$ node ./distHello World! ClassOne and ClassTwo;)--ClassOne property is of type ClassTwoClassTwo property is of type undefined
Explanation:
Well it's not much a surprise, we are running into a circular import issue:
index.ts is importing ClassOne
ClassOne is importing ClassTwo
ClassTwo is importing ClassOne
Since ClassOne is being read, return the cached value of ClassOne
__metadata("design:type", classOne_1.default) is evaluated
classOne_1.default is evaluated to undefined
...
Workaround:
There is a simple workaround and it's already used by many libraries but unfortunately not by typescript. The idea is not to emit the type but to emit a type resolver ie. instead of emitting ClassFoo one should emit () => ClassFoo. This way we can evaluate the type resolver in index.ts at a time where ClassOne import is already done.
This issue is the root of many issues. Solving this will enable a much better code where reflection is used by not introducing the type resolver as a redondancy.
Please note that trying to solve the circular import is not a good solution. Sometimes circular import are inherents and it's very hard to both focus on code and trying to solve every "circular import" related problems. tslib would benefit a lot from being more resilient to such issues.
The text was updated successfully, but these errors were encountered:
TypeScript Version: 4.1.0-dev.20201022
Search Terms: emitDecoratorMetadata, decorators, design:type, type, resolver, reflection
Code:
Suppose we are doing Node.js code and have a plain old circular dependency:
classOne.ts
classTwo.ts
and suppose we want to bring a bit of reflection in our code:
index.ts
utils.ts
Expected behavior:
Actual behavior:
Explanation:
Well it's not much a surprise, we are running into a circular import issue:
__metadata("design:type", classOne_1.default)
is evaluatedclassOne_1.default
is evaluated to undefinedWorkaround:
There is a simple workaround and it's already used by many libraries but unfortunately not by typescript. The idea is not to emit the type but to emit a type resolver ie. instead of emitting
ClassFoo
one should emit() => ClassFoo
. This way we can evaluate the type resolver in index.ts at a time where ClassOne import is already done.Playground Link:
https://github.com/paeolo/typescript-emit-resolver
Related Issues:
This issue is the root of many issues. Solving this will enable a much better code where reflection is used by not introducing the type resolver as a redondancy.
Please note that trying to solve the circular import is not a good solution. Sometimes circular import are inherents and it's very hard to both focus on code and trying to solve every "circular import" related problems. tslib would benefit a lot from being more resilient to such issues.
The text was updated successfully, but these errors were encountered: