Typescript + Babel 7

Has anyone managed to run Aurelia with both Typescript (esnext) + Babel 7 (as explained in this post: https://iamturns.com/typescript-babel/). I’ve gotten pretty far, but have problems now with the @autoinject, it doesn’t seem like any of the dependencies are injected. No errors just undefined values in the constructor.
I’m using both
["@babel/plugin-proposal-decorators", { “legacy”: true }]
["@babel/plugin-proposal-class-properties", { “loose”: true }]

But before spending more time on this I was a bit curious to check here if this is supposed to work at all with Aurelia?

1 Like

Is there a way to make TypeScript compile the decorator first before handling to Babel? I guess your issues comes from a configuration that makes TS only strip typings, leaving decorator compilation entirely to babel.

Also the post you gave doesn’t have emit decorator metadata in tsconfig i guess, can you try again with that turned on?

So how does Aurelia do this? Is the goal here to have the decorators written in the resulting code so that Aurelia can find them at runtime? I’m trying to toggle things on and off at random. Most things in tsconfig doesn’t seems to have an effect. But when changing the legacy flag to false in the babel plugin, then heaps of decorator errors starts to popup.

My tsconfig currently has this:
{
“compilerOptions”: {
“allowJs”: true,
“target”: “esnext”,
“noEmit”: true,
“isolatedModules”: true,
“emitDecoratorMetadata”: true,
“experimentalDecorators”: true,
“moduleResolution”: “node”,
“esModuleInterop”: true,
“lib”: [“es2017”, “dom”],
“baseUrl”: “./src”
},
“include”: [“src//*", "custom_typings//*”],
“atom”: {
“rewriteTsconfig”: false
}
}

1 Like

hm, that acctually wouldn’t make sense, decorators is just some syntactic sugar so I guess they need to be compiled by babel in the end before sent to aurelia and the browser

1 Like

Aurelia doesn’t do anything beside consuming decorator metadata emitted by TS, to properly register dependencies for injection. That’s what @autoinject needs in order to work. Consider following examples:

@autoinject
export class MyEl {
  constructor(element: Element, ea: EventAggregator) {

  }
}

It should be compiled to something like the following made up code, with emitDecoratorMetadata on:

export class MyEl {
  ...
}
__metadata(MyEl, 'design:parameter', [0], Element)
__metadata(MyEl, 'design:parameter', [1], EventAggregator);

Those __metadata function call later will be used by autoinject as you can see from here https://github.com/aurelia/dependency-injection/blob/587de79125fb8e7cca54a5d4785ccebf28c90954/src/injection.js#L10 to create static inject property on the class

...
target.inject = (metadata.getOwn(metadata.paramTypes, target) || _emptyParameters).slice();
...

So it’s all about ensuring decorator metadata is emitted properly. Or, you can just use @inject if the setup combo proven to be difficult.

Babel 7 doesn’t emit decorator metadata by default.

Check out this Stackoverflow answer for a plugin to add support to Babel 7.

It’s also worth noting that because Babel 7 strips/ignores types completely, it doesn’t support TypeScripts const enum.

3 Likes

I’ve found the same issue while working with other packages depending on Reflect.metadata and TSC decorators metadata. In order to enable this even with @babel/preset-typescript i’ve developed a plugin (transform) for babel outputting a compatible code.
I’m using it in a project of ours, but I’d like to share it in order to make sure everything is fully functional / receive feedback by other members of the community. Hope this is helping you! :+1:

2 Likes

It appears I ran into this same problem. If one prefers robustness, would it be better to first run TS (with exnext) first and then Babel on the results to transpile down if needed? I could imagine the difference is mostly about performance and then some build configuration complexity.

1 Like