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
{{ message }}
Repository navigation
Support typescript with --experimental-strip-types #208
Add this issue to keep track of the work for supporting typescript out of the box, in the PR you can find documentation etc...
The main idea is to use type-stripping.
This is achieved ideally through the use of an external dependency, in my opinion @swc/wasm-typescript.
I would like to have something basic and usable to propose to the public.
Tracking points to discuss:
Support commonjs syntax
SourceMaps (enabling them with a flag)
Type stripping on .cts
Add a flag to enable transformation to support TS only features (enums etc...)
Extension guessing (.js because of transpilation => but at runtime is a TS)
Is swc on board with following our LTS? This seems a massive dependency we should not take on lightly.
TypeScript does not follow semver, and the language evolves relatively quickly. Is this approach stable long term? Everybody seems always to be supporting the latest version of TypeScript.
My proposal in nodejs/node#43818 was to have a strategy for doing this automatically but not vendor anything. Something along the lines of:
$ node script.ts
Typescript support is missing, install it with:
npm i --location=global typescript-node-core
$ npm i --location=global typescript-node-core
...
$ node script.ts
"Hello World"
An alternative (possibly better for long-term support):
$ node script.ts
Typescript support is missing, install it with:
npx ts-node-core
Learn about TypeScript support at: ...
$ npx ts-node-core
Detected Node.js v24.42.0
What typescript support would you like?
[ ] type stripping - swc@22
[ ] type checking - tsc@23
...Installing
$ node script.ts
"Hello World"
SWC seems to be a mature project, it is already used by Deno for our same purpose, has Apache-2 license which is fine.
I'm using @swc/wasm, the wasm version to avoid compiling rust. We probably should ask the blessing from the maintainer @kdy1 and maybe release a version that we dont have to patch 😄
With typestripping we dont really care what version of typescript the user is using because we just remove it (unless there is some syntax not supported by SWC), we dont perform type checking at all. I think SWC moves quickly enough.
Also asking the user to install a dependency is imho a suboptimal DX, users just want to be able to run node foo.ts
If this proposal is accepted we could also add hooks to customizate the transpilation, and many more things that come out of the box from swc
I can configure a publish pipeline for a separate package/binary, or a Wasm publish pipeline if node.js will use SWC for this task.
Reacted by Marco Ippolito, Matteo Collina, Benjamin Gruenbaum, Stephen Belanger, Jacob Smith, Balázs Orbán, Andrew McClenaghan, Anthony Shew, Hiroki Osame, v1rtl and 8 more
If what we ship is type stripping, then we’re essentially shipping experimental support for the Type Annotations proposal. Which is good! I feel like that’s what does make sense to include in Node core, and I hope that proposal graduates and V8 adds support and then we can drop our support for doing it ourselves.
In that vein, maybe a flag of --experimental-strip-types might make more sense? To clarify to users that our intended scope is not to support all of TypeScript’s syntax or features, such as paths or enums or type checking; we just strip types and that’s it, and if they want more they can register module customization hooks from a userland library. And type stripping should be stable and unlikely to change as TypeScript evolves.
Reacted by Jacob Smith, Benjamin Gruenbaum and Linus Unnebäck
If what we ship is type stripping, then we’re essentially shipping experimental support for the Type Annotations proposal. Which is good! I feel like that’s what does make sense to include in Node core, and I hope that proposal graduates and V8 adds support and then we can drop our support for doing it ourselves.
In that vein, maybe a flag of --experimental-strip-types might make more sense? To clarify to users that our intended scope is not to support all of TypeScript’s syntax or features, such as paths or enums or type checking; we just strip types and that’s it, and if they want more they can register module customization hooks from a userland library. And type stripping should be stable and unlikely to change as TypeScript evolves.
Not against renaming the flag, but for this very limited features that we are adding, --experimental-typescript makes more sense because we are actually only supporting .ts files, (no tsx or other flavors), but once this lands and remove the flag, we will think on how to make it customizable, and increase the number of flavor, that would be a real type-stripping
I think there was general agreement that type stripping (but probably not more) is something that we would like to see in core, either soon or eventually.
@joyeecheung asked that @marco-ippolito’s branch be implemented as a package similar to Undici that can be developed outside of the core release cycle and eventually merged in when it’s mature.
To exist in core, support would need to both take advantage of being in core, such as being native; and avoid implementing features of TypeScript that may change faster than our release cycle (such as type checking, and any transforms such as enums, decorators, ESM-CJS transpilation and others).
@legendecas will schedule a meeting with @marco-ippolito and some champions of the Type Annotations proposal to discuss collaboration and next steps toward potentially implementing that proposal in Node core.
I saw that. What settings would I use to just strip types and nothing more? No support for enums, decorators, transpiling module import/export, etc. As in, if I wanted to use this to implement the Type Annotations proposal and error on any syntax that requires transforms.
catering to such more general users seems to be the original goal of this effort.
I was the one who defined the original goal of this effort 😄 At least when we first starting discussing type-stripping a few weeks ago. The goal was to find something that we could ship within Node that would survive a three-year LTS period, despite TypeScript itself not following semver. At the time we assumed that TypeScript transforms would need to be ruled out because we assumed that they weren’t stable enough for a three-year lifespan, and fortunately the TypeScript team has informed us that that isn’t the case, so it’s an option to support them; but still, supporting them incurs a performance penalty. Perhaps less of a performance penalty if they can be transformed with the goal of trying to preserve line and column numbers, saving us from needing to generate a source map, but that’s a challenge for SWC and out of scope for Node.
Would you say the existence of something like this will make the TSC more willing to consider full TypeScript syntax support?
I can only speak for myself, not the TSC. I think you need to look at what some of the TypeScript team have been saying, though: “full” TypeScript support isn’t just type-stripping plus transforms, it’s also type checking and supporting all the options within tsconfig.json. Node can’t ever include all of that in core, because the type checking and tsconfig options very much do change within a 3-year cycle. So even if we support type stripping plus transforms, we’re still going to leave many users unhappy because of the other things that get left out.
And the solution for those users is to just use TypeScript in full, via tsc or --import=tsx or Vite or something similar. Those solutions already exist and will continue to exist, and Node will continue to support and encourage them; our error messages and docs will point people in their direction to get whatever features they want that Node can’t provide. Fundamentally it’s just not possible to satisfy both of the user requests of “full” TypeScript and no breaking changes for a three year LTS cycle. Either the TypeScript will get very out of date or it won’t be stable, and I consider both of those options to be worse than the two-option menu of type-stripping plus hooks for external tools.
I can imagine you asking “so if bundling the full TypeScript experience is off the table, what about the semi-full TypeScript of just type-stripping plus transforms?” And my reply is to just look at the work I did to migrate Corepack. Maybe 5% of it was dealing with the public class property and the enum, and I could’ve migrated the enum in a much simpler way than I did. Much more time was spent on updating import specifiers, though I’m sure that could be automated (and migrating most if not all syntaxes like enums could probably be automated too). In other words, transforms aren’t much of an issue, and they have modern equivalents that are strippable, so I think it’s a better tradeoff to ban them and get faster performance than support something that could be deprecated. It’s a certainty that any built-in TypeScript support that Node has won’t be able to run all existing TypeScript code, so if people are going to need to migrate at all (whether by hand or via a tool) they might as well migrate to the most performant syntaxes rather than Node sacrificing some performance to save some of the migration work. And finally, our target audience for this feature is users starting new projects, not users migrating existing ones, as the latter users by definition already have a solution that works for them. New users starting from scratch can work within the constraints of type stripping if that’s how they want to run their TypeScript, and presumably an ecosystem will grow with linting tools and the like for helping guide them. And for everyone else, as I keep saying, tsc and tsx and Vite and tsimp and ts-node and so on will continue to exist and continue to work, and continue providing fuller experiences than Node ever can.
First of all, by "full syntax support" I mean a transform-only support, like what esbuild / swc / oxc does with isolatedModules: true. Never intended to want type-checking, tsconfig options etc.
Perhaps less of a performance penalty if they can be transformed with the goal of trying to preserve line and column numbers, saving us from needing to generate a source map, but that’s a challenge for SWC and out of scope for Node.
It's not out of scope. If such transform exists with negligible perf overhead compared to strip-types-only, then the argument for Node.js to go with strip-types-only becomes much weaker. In fact, I am raising this question because I am very confident this is something feasible, therefore the assumption that "strip-types-only has big perf wins" needs to be challenged.
Out of curiosity I ran the same benchmark on your modified corepack source using oxc-node:
Benchmark 1: node --import @oxc-node/core/register ./corepack/sources/_cli.ts
Time (mean ± σ): 93.4 ms ± 1.4 ms [User: 99.8 ms, System: 13.4 ms]
Range (min … max): 91.4 ms … 96.7 ms 30 runs
Benchmark 2: node --import tsx ./corepack/sources/_cli.ts
Time (mean ± σ): 136.0 ms ± 0.8 ms [User: 161.1 ms, System: 21.4 ms]
Range (min … max): 134.0 ms … 137.5 ms 30 runs
Summary
node --import @oxc-node/core/register ./corepack/sources/_cli.ts ran
1.46 ± 0.02 times faster than node --import tsx ./corepack/sources/_cli.ts
The perf is 1.46x over tsx vs the 1.22x of current --experimental-strip-types. Granted this is native binary vs wasm, but this one is actually running a full transform with source maps, and even respecting resolution config in tsconfig.json. Even both as native binary, an oxc-based position-preserving + no AST allocation TS transform can very possibly out perform other strip-type-only implementations. SWC can also implement the proposed transform on top of the current strip-type-only implementation and compare the result.
In other words, transforms aren’t much of an issue
I fundamentally disagree with this. Removing these small discrepancies avoids a Node-flavored subset of TS. Right now these discrepancies are justified in the name of performance - but as I said, the assumption that "strip-types-only has big perf wins" needs to be challenged.
and presumably an ecosystem will grow with linting tools and the like for helping guide them.
This is encouraging yet another flavor of TS that only works in Node, creating more fragmentation / confusion in an already complicated landscape, but the other side of the tradeoff is questionable for the reasons outlined above.
"Node.js supports full syntax-level TS transforms, but not type checking / tsconfig" is a much cleaner definition than "Node.js supports a subset of TS syntax". It aligns with all the other transforms users are already using via esbuild / swc / oxc etc.
My whole argument is that it is not a good idea to intentionally steer away from possible ecosystem alignment in the name of performance, especially when the performance gain might not be as significant as imagined.
Reacted by Shinebayar G, Théo LUDWIG, Boonyasuk Dheeravongkit, Torsten Rüter and Stefan
I think you need to look at what some of the TypeScript team have been saying, though: “full” TypeScript support isn’t just type-stripping plus transforms, it’s also type checking and supporting all the options within tsconfig.json
I don't think anyone has actually advocated for checking; that is of course the bit that "doesn't follow semver", though the general scheme is that "any change could be a breaking change". The config defaults are what change in semver major, and the API is otherwise very stable. Not that using TS itself here is a goal; any third party emitter can handle TS code if it complies with isolatedModules.
I mean a transform-only support, like what esbuild / swc / oxc does with isolatedModules: true. Never intended to want type-checking, tsconfig options etc.
Note that there are a couple of options respected by those tools which affect emit, namely useDefineForClassFields (and target after a specific version) and experimentalDectorators. These aren't affected by isolatedModules, and I'm not sure what swc does at this point (I think it leaves define class field semantics on by default, differing to TSC?)
My whole argument is that it is not a good idea to intentionally steer away from possible ecosystem alignment in the name of performance, especially when the performance gain might not be as significant as imagined.
Sure. Right now, though, the transpiler you describe that can do transforms yet still not need source maps doesn’t exist, as far as I know. If someone builds that, and we measure the performance difference and it’s truly negligible, then it’s something to consider. We can always add support for transforms later, even after this goes stable.
But my point about looking at the changeset for the Corepack migration was that transforms account for very little of it, and the rest will still remain even if we support transforms: adding file extensions, adding with { type: 'json' }, using import type, etc. There are also some potential migrations that Corepack didn’t need but that many apps would, of avoiding tsconfigpaths and adjusting to ESM-syntax code running as ESM rather than being transpiled into CommonJS (so no global require intermixed with import/export, etc.). All of these Node-specific idiosyncracies would remain even if we start supporting transforms. Maybe some of them we might find ways to get rid of, but I doubt we’d eliminate all of them.
And so then yes, Node’s built-in support will support a particular subset of TypeScript’s full feature set, and users wanting to use that support instead of tsc or tsx or Vite etc will need to live within those limitations. Probably everything that would need to change could be automated, from adding file extensions to splitting import/import type to migrating from tsconfigpaths to package.json"imports" and so on. And if that’s the case, I’m not sure why transforms are such a concern as compared with any of the other things that ecosystem tools might support that Node might not.
If someone builds that, and we measure the performance difference and it’s truly negligible, then it’s something to consider. We can always add support for transforms later, even after this goes stable.
Great. That's pretty much what I want to hear.
Surely there will be some migration costs that cannot be covered by transforms, but don't we agree that the smaller the differences are, the better?
More importantly we need to evaluate these differences in context - things like tsconfig paths are much easier to explain because Node.js' own module resolutions rules need to be the source of truth, but explaining that
classFoo{construct(publicid: number){}}
...works as expected everywhere else but just not in Node.js (which claims to "support TypeScript") seems... very awkward.
Reacted by Matt Pocock, Tyler Steinhaus, Thai Pangsakulyanont and Boonyasuk Dheeravongkit
I didn't ask whether the migration was hard to someone well versed in Node.js. I did ask however:
Who is this feature built for? What is the intended audience?
Why is this feature designed by default as a departure from status quo?
Why have other tools been able to support transforms, and Node.js would be unable to?
Why is Node.js taking ownership of the syntax allowed in a .ts file?
What are examples of packages shipped to run as TS, and do they use enums?
As for whether the migration would be easy or hard, that matters much less to me than having a migration. As designed, the feature would force people out of specific TS syntax constructs, which seems an overreach to me.
The decorator pass is too complex to be supported with this kind of transpilation. There's no way to preserve token order.
Constructor properties
I'm not sure if this is possible or not, but my guess is that it's impossible in some cases, meaning trying to support it may result in forcing specific formatting for the users.
Since decorators are on standards track and already stage 3, I don’t think they need to be covered by the TS transform in the long run. Short term it may be the only exception.
I'm willing to support full ts features, behind a flag, if we see it works well and it's stable, we will as well remove the flag and set it to default.
Example:
The current swc version does not support this so it's gonna be in a next iteration.
If we can make it work without source-maps and with whitespacing it would be even better
Who is this feature built for? What is the intended audience?
Users writing scripts without package.json or dependencies, where the user wants to use TypeScript syntax.
Users starting new projects where they can write using type stripping-compatible TypeScript syntax/settings from the start, who prefer the speed of built-in native compilation. (We're using Wasm SWC currently, but the intention is to replace it with a native build once we can figure out how to get Rust into our build chain.)
Why is this feature designed by default as a departure from status quo?
Node already supports the “status quo”, via --import=tsx.
One of the primary reasons that anything gets included in core as opposed to being left to userland is because the feature can be faster when included in core, usually by being implemented using native code. Especially since TypeScript via hooks is already possible, and that supports the TypeScript that users are already familiar with, this built-in support should be optimized for performance.
The proposed behavior matches the Type Annotations proposal, which might become a future status quo (and which many of us support). If that occurs, then type stripping will just become standard JavaScript, and code written for type stripping today will work in any runtime including browsers; and Node will be able to simplify our internals by removing SWC, since V8 would handle the type stripping for us.
Why have other tools been able to support transforms, and Node.js would be unable to?
Node already supports transforms, via --import=tsx.
Node could support transforms via its built-in support as well, either enabled all the time or via a separate opt-in flag, but I think we would want to do so only if performance were not noticeably impacted. A main advantage of the built-in support is theoretically improved performance, so we wouldn’t want to noticeably degrade that to support features that users can already access via --import=tsx. Or put another way: if type stripping lands, Node will have two methods for supporting TypeScript. It’s better if one of them is fast, even if slightly limited, rather than both of them being slow and equivalent.
Transforms are not part of the Type Annotations proposal, so supporting them muddies the transition to that potential future where that proposal graduates. Since transforms are already supported via --import=tsx, it might be preferable to keep type-stripping aligned with that proposal as it advances, at least until it either graduates or is abandoned.
Why is Node.js taking ownership of the syntax allowed in a .ts file?
Because no one else is? Has there been a proposal from the TypeScript team for adding built-in support of TypeScript in Node?
Type stripping is primarily inspired by the Type Annotations proposal, of which TypeScript’s @DanielRosenwasser is a champion. This is arguably just as blessed a syntax or standard as the moving target of whatever a given version of tsc supports.
What are examples of packages shipped to run as TS, and do they use enums?
I don’t know of any. As the TypeScript team has explained, shipping packages written in TypeScript is generally a bad idea, unless you’re in control of your consumers (for example, if it’s meant for use only by you in your own app, or for the members of your team). Even if there are packages out there that don’t use enums, they probably don’t follow all of the rules of --experimental-type-stripping (see the things I changed in Corepack). The goal isn’t to support existing published packages written in TypeScript, if there are any.
The proposed behavior matches the Type Annotations proposal, which might become a future status quo (and which many of us support). If that occurs, then type stripping will just become standard JavaScript, and code written for type stripping today will work in any runtime including browsers; and Node will be able to simplify our internals by removing SWC, since V8 would handle the type stripping for us.
Type stripping is primarily inspired by the Type Annotations proposal, of which TypeScript’s @DanielRosenwasser is a champion. This is arguably just as blessed a syntax or standard as the moving target of whatever a given version of tsc supports.
I would still shy away from this as a motivator; the Type Annotations proposal may not cover all TS syntax. Not only that, but it can't, because in TypeScript the expression foo<number>(value) is a function call with type arguments, but in JavaScript that's two comparisons. This means that the type annotations proposal will never contain all valid TypeScript syntax today. As such --experimental-strip-types would a different subset of the language (so not "matching" the TC39 proposal), and can't be eventually replaced by something from V8.
@marco-ippolito I want to help as much as I can to get this feature working thoroughly before 2025. I have nothing but free time. Please let me know how I can help.
Activity
mcollina commentedon Jul 2, 2024
I have two concerns (none of them blocking):
My proposal in nodejs/node#43818 was to have a strategy for doing this automatically but not vendor anything. Something along the lines of:
$ node script.ts Typescript support is missing, install it with: npm i --location=global typescript-node-core $ npm i --location=global typescript-node-core ... $ node script.ts "Hello World"An alternative (possibly better for long-term support):
marco-ippolito commentedon Jul 2, 2024
To answer to your points:
I'm using @swc/wasm, the wasm version to avoid compiling rust. We probably should ask the blessing from the maintainer @kdy1 and maybe release a version that we dont have to patch 😄
Also asking the user to install a dependency is imho a suboptimal DX, users just want to be able to run
node foo.tsIf this proposal is accepted we could also add hooks to customizate the transpilation, and many more things that come out of the box from swc
kdy1 commentedon Jul 2, 2024
I can configure a publish pipeline for a separate package/binary, or a Wasm publish pipeline if node.js will use SWC for this task.
marco-ippolito commentedon Jul 2, 2024
If we are able to ship the initial PR with v1.6.6 for the next updates that would be amazing, thank you
benjamingr commentedon Jul 2, 2024
+1 for all the suggestions and also +1 for @kdy1 have nothing but nice things to say about him and swc + there is prior art in other runtimes.
GeoffreyBooth commentedon Jul 2, 2024
If what we ship is type stripping, then we’re essentially shipping experimental support for the Type Annotations proposal. Which is good! I feel like that’s what does make sense to include in Node core, and I hope that proposal graduates and V8 adds support and then we can drop our support for doing it ourselves.
In that vein, maybe a flag of
--experimental-strip-typesmight make more sense? To clarify to users that our intended scope is not to support all of TypeScript’s syntax or features, such as paths or enums or type checking; we just strip types and that’s it, and if they want more they can register module customization hooks from a userland library. And type stripping should be stable and unlikely to change as TypeScript evolves.marco-ippolito commentedon Jul 2, 2024
Not against renaming the flag, but for this very limited features that we are adding,
--experimental-typescriptmakes more sense because we are actually only supporting.tsfiles, (notsxor other flavors), but once this lands and remove the flag, we will think on how to make it customizable, and increase the number of flavor, that would be a realtype-strippingGeoffreyBooth commentedon Jul 2, 2024
This was discussed today in the loaders meeting:
kdy1 commentedon Jul 3, 2024
I created https://www.npmjs.com/package/@swc/wasm-typescript, which barely strips out the type.
And I wrote documentation for it at https://swc.rs/docs/references/wasm-typescript
GeoffreyBooth commentedon Jul 3, 2024
How can this be used to just strip types and nothing more?
kdy1 commentedon Jul 3, 2024
See https://swc.rs/docs/references/wasm-typescript
GeoffreyBooth commentedon Jul 3, 2024
I saw that. What settings would I use to just strip types and nothing more? No support for enums, decorators, transpiling module import/export, etc. As in, if I wanted to use this to implement the Type Annotations proposal and error on any syntax that requires transforms.
kdy1 commentedon Jul 3, 2024
It's not
transform()of@swc/core. It's a completely different thing.It
These operations is one
typescriptset, but I can remove the enum part and/or handling code for type-only imports.I'll add options for them soon.
65 remaining items
GeoffreyBooth commentedon Jul 14, 2024
I was the one who defined the original goal of this effort 😄 At least when we first starting discussing type-stripping a few weeks ago. The goal was to find something that we could ship within Node that would survive a three-year LTS period, despite TypeScript itself not following semver. At the time we assumed that TypeScript transforms would need to be ruled out because we assumed that they weren’t stable enough for a three-year lifespan, and fortunately the TypeScript team has informed us that that isn’t the case, so it’s an option to support them; but still, supporting them incurs a performance penalty. Perhaps less of a performance penalty if they can be transformed with the goal of trying to preserve line and column numbers, saving us from needing to generate a source map, but that’s a challenge for SWC and out of scope for Node.
I can only speak for myself, not the TSC. I think you need to look at what some of the TypeScript team have been saying, though: “full” TypeScript support isn’t just type-stripping plus transforms, it’s also type checking and supporting all the options within
tsconfig.json. Node can’t ever include all of that in core, because the type checking andtsconfigoptions very much do change within a 3-year cycle. So even if we support type stripping plus transforms, we’re still going to leave many users unhappy because of the other things that get left out.And the solution for those users is to just use TypeScript in full, via
tscor--import=tsxor Vite or something similar. Those solutions already exist and will continue to exist, and Node will continue to support and encourage them; our error messages and docs will point people in their direction to get whatever features they want that Node can’t provide. Fundamentally it’s just not possible to satisfy both of the user requests of “full” TypeScript and no breaking changes for a three year LTS cycle. Either the TypeScript will get very out of date or it won’t be stable, and I consider both of those options to be worse than the two-option menu of type-stripping plus hooks for external tools.I can imagine you asking “so if bundling the full TypeScript experience is off the table, what about the semi-full TypeScript of just type-stripping plus transforms?” And my reply is to just look at the work I did to migrate Corepack. Maybe 5% of it was dealing with the public class property and the enum, and I could’ve migrated the enum in a much simpler way than I did. Much more time was spent on updating import specifiers, though I’m sure that could be automated (and migrating most if not all syntaxes like enums could probably be automated too). In other words, transforms aren’t much of an issue, and they have modern equivalents that are strippable, so I think it’s a better tradeoff to ban them and get faster performance than support something that could be deprecated. It’s a certainty that any built-in TypeScript support that Node has won’t be able to run all existing TypeScript code, so if people are going to need to migrate at all (whether by hand or via a tool) they might as well migrate to the most performant syntaxes rather than Node sacrificing some performance to save some of the migration work. And finally, our target audience for this feature is users starting new projects, not users migrating existing ones, as the latter users by definition already have a solution that works for them. New users starting from scratch can work within the constraints of type stripping if that’s how they want to run their TypeScript, and presumably an ecosystem will grow with linting tools and the like for helping guide them. And for everyone else, as I keep saying,
tscandtsxand Vite andtsimpandts-nodeand so on will continue to exist and continue to work, and continue providing fuller experiences than Node ever can.yyx990803 commentedon Jul 14, 2024
First of all, by "full syntax support" I mean a transform-only support, like what esbuild / swc / oxc does with
isolatedModules: true. Never intended to want type-checking, tsconfig options etc.It's not out of scope. If such transform exists with negligible perf overhead compared to strip-types-only, then the argument for Node.js to go with strip-types-only becomes much weaker. In fact, I am raising this question because I am very confident this is something feasible, therefore the assumption that "strip-types-only has big perf wins" needs to be challenged.
Out of curiosity I ran the same benchmark on your modified corepack source using oxc-node:
The perf is 1.46x over
tsxvs the 1.22x of current--experimental-strip-types. Granted this is native binary vs wasm, but this one is actually running a full transform with source maps, and even respecting resolution config intsconfig.json. Even both as native binary, an oxc-based position-preserving + no AST allocation TS transform can very possibly out perform other strip-type-only implementations. SWC can also implement the proposed transform on top of the current strip-type-only implementation and compare the result.I fundamentally disagree with this. Removing these small discrepancies avoids a Node-flavored subset of TS. Right now these discrepancies are justified in the name of performance - but as I said, the assumption that "strip-types-only has big perf wins" needs to be challenged.
This is encouraging yet another flavor of TS that only works in Node, creating more fragmentation / confusion in an already complicated landscape, but the other side of the tradeoff is questionable for the reasons outlined above.
"Node.js supports full syntax-level TS transforms, but not type checking / tsconfig" is a much cleaner definition than "Node.js supports a subset of TS syntax". It aligns with all the other transforms users are already using via esbuild / swc / oxc etc.
My whole argument is that it is not a good idea to intentionally steer away from possible ecosystem alignment in the name of performance, especially when the performance gain might not be as significant as imagined.
jakebailey commentedon Jul 14, 2024
I don't think anyone has actually advocated for checking; that is of course the bit that "doesn't follow semver", though the general scheme is that "any change could be a breaking change". The config defaults are what change in semver major, and the API is otherwise very stable. Not that using TS itself here is a goal; any third party emitter can handle TS code if it complies with isolatedModules.
Note that there are a couple of options respected by those tools which affect emit, namely useDefineForClassFields (and target after a specific version) and experimentalDectorators. These aren't affected by isolatedModules, and I'm not sure what swc does at this point (I think it leaves define class field semantics on by default, differing to TSC?)
GeoffreyBooth commentedon Jul 14, 2024
Sure. Right now, though, the transpiler you describe that can do transforms yet still not need source maps doesn’t exist, as far as I know. If someone builds that, and we measure the performance difference and it’s truly negligible, then it’s something to consider. We can always add support for transforms later, even after this goes stable.
But my point about looking at the changeset for the Corepack migration was that transforms account for very little of it, and the rest will still remain even if we support transforms: adding file extensions, adding
with { type: 'json' }, usingimport type, etc. There are also some potential migrations that Corepack didn’t need but that many apps would, of avoidingtsconfigpathsand adjusting to ESM-syntax code running as ESM rather than being transpiled into CommonJS (so no globalrequireintermixed withimport/export, etc.). All of these Node-specific idiosyncracies would remain even if we start supporting transforms. Maybe some of them we might find ways to get rid of, but I doubt we’d eliminate all of them.And so then yes, Node’s built-in support will support a particular subset of TypeScript’s full feature set, and users wanting to use that support instead of
tscortsxor Vite etc will need to live within those limitations. Probably everything that would need to change could be automated, from adding file extensions to splittingimport/import typeto migrating fromtsconfigpathstopackage.json"imports"and so on. And if that’s the case, I’m not sure why transforms are such a concern as compared with any of the other things that ecosystem tools might support that Node might not.yyx990803 commentedon Jul 14, 2024
Great. That's pretty much what I want to hear.
Surely there will be some migration costs that cannot be covered by transforms, but don't we agree that the smaller the differences are, the better?
More importantly we need to evaluate these differences in context - things like tsconfig paths are much easier to explain because Node.js' own module resolutions rules need to be the source of truth, but explaining that
...works as expected everywhere else but just not in Node.js (which claims to "support TypeScript") seems... very awkward.
marco-ippolito commentedon Jul 14, 2024
@kdy1 do you think is possible to perform transformation of ts features and preserve the position in the file with whitespacing?
arcanis commentedon Jul 14, 2024
I didn't ask whether the migration was hard to someone well versed in Node.js. I did ask however:
.tsfile?As for whether the migration would be easy or hard, that matters much less to me than having a migration. As designed, the feature would force people out of specific TS syntax constructs, which seems an overreach to me.
kdy1 commentedon Jul 14, 2024
@marco-ippolito It's partially possible, but it's a bit hard in general.
Basic restrictions are
We need to evaluate each syntax under this assumption.
Enums
Enums are typically written as
and it can be transpiled as
so it can be supported.
Decorators
The decorator pass is too complex to be supported with this kind of transpilation. There's no way to preserve token order.
Constructor properties
I'm not sure if this is possible or not, but my guess is that it's impossible in some cases, meaning trying to support it may result in forcing specific formatting for the users.
If you have some ideas, please share
yyx990803 commentedon Jul 14, 2024
@kdy1 I shared an example here: #208 (comment)
Since decorators are on standards track and already stage 3, I don’t think they need to be covered by the TS transform in the long run. Short term it may be the only exception.
marco-ippolito commentedon Jul 14, 2024
I'm willing to support full ts features, behind a flag, if we see it works well and it's stable, we will as well remove the flag and set it to default.
Example:
And with source maps support
The current swc version does not support this so it's gonna be in a next iteration.
If we can make it work without source-maps and with whitespacing it would be even better
GeoffreyBooth commentedon Jul 14, 2024
package.jsonor dependencies, where the user wants to use TypeScript syntax.--import=tsx.--import=tsx.--import=tsx. Or put another way: if type stripping lands, Node will have two methods for supporting TypeScript. It’s better if one of them is fast, even if slightly limited, rather than both of them being slow and equivalent.--import=tsx, it might be preferable to keep type-stripping aligned with that proposal as it advances, at least until it either graduates or is abandoned.tscsupports.I don’t know of any. As the TypeScript team has explained, shipping packages written in TypeScript is generally a bad idea, unless you’re in control of your consumers (for example, if it’s meant for use only by you in your own app, or for the members of your team). Even if there are packages out there that don’t use enums, they probably don’t follow all of the rules of
--experimental-type-stripping(see the things I changed in Corepack). The goal isn’t to support existing published packages written in TypeScript, if there are any.jakebailey commentedon Jul 15, 2024
I would still shy away from this as a motivator; the Type Annotations proposal may not cover all TS syntax. Not only that, but it can't, because in TypeScript the expression
foo<number>(value)is a function call with type arguments, but in JavaScript that's two comparisons. This means that the type annotations proposal will never contain all valid TypeScript syntax today. As such--experimental-strip-typeswould a different subset of the language (so not "matching" the TC39 proposal), and can't be eventually replaced by something from V8.marco-ippolito commentedon Jul 25, 2024
Since this has landed and we have a roadmap I'll close this issue, we can talk about next steps here #217
sdegutis commentedon Aug 10, 2024
@marco-ippolito I want to help as much as I can to get this feature working thoroughly before 2025. I have nothing but free time. Please let me know how I can help.