GeezerVison is showing github.com — module: add --experimental-strip-types by marco-ippolito · Pull Request #53725 · nodejs/node
Conversation
It is possible to execute TypeScript files by setting the experimental flag --experimental-strip-types.
Node.js will transpile TypeScript source code into JavaScript source code.
During the transpilation process, no type checking is performed, and types are discarded.
Roadmap
Refs: nodejs/loaders#217
Motivation
I believe enabling users to execute TypeScript files is crucial to move the ecosystem forward, it has been requested on all the surveys, and it simply cannot be ignored. We must acknowledge users want to run node foo.ts without installing external dependencies or loaders.
There is a TC39 proposal for type annotations
Why type stripping
Type stripping as the name suggest, means removing all the types, transform the input in a JavaScript module.
const foo: string = "foo";
Becomes:
Other runtimes also perform transformation of some TypeScript only features into JavaScript, for example enums, which do not exists in JavaScript.
At least initially in this PR no trasformation is performed, meaning that using Enum, namespaces etc... will not be possible.
Why I chose @swc/wasm-typescript
Because of simplicity.
I have considered other tools but they require either rust or go to be added to the toolchain.
@swc/wasm-typescript its a small package with a wasm and a js file to bind it.
Swc is currently used by Deno for the same purpose, it's battle tested.
In the future I see this being implemented in native layer.
Massive shoutout to @kdy1 for releasing a swc version for us.
⚠️ Refer to the PR changes in typescript.md for implementation details and limitations.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
benjamingr, fabiospampinato, ArturJS, niieani, karlhorky, RobertoSimonini1, juancarlosjr97, raulmarindev, hardfist, AugustinMauroy, and 660 more reacted with thumbs up emoji
kkocdko, latin-1, vrtnis, azizsigar, madhums, kungfooman, egasimus, Oleg2tor, donatj, Flrande, and 10 more reacted with thumbs down emoji
darrylyeo, joansebgarcia, paulmelero, ArthurKun21, collin, Klaster1, jedieaston, Fannon, mdrobny, vrtnis, and 151 more reacted with hooray emoji
karlhorky, anuraghazra, trivikr, SuperchupuDev, kibertoad, atlowChemi, felixfbecker, gabro, vecerek, tomwatkins1994, and 251 more reacted with heart emoji
daeyeon, legendecas, ronag, karlhorky, anuraghazra, SuperchupuDev, RomainLanz, shrujalshah28, balazsorban44, kibertoad, and 180 more reacted with rocket emoji
karlhorky, anuraghazra, SuperchupuDev, wojtekmaj, juancarlosjr97, eseiker, o-az, SukkaW, jaydenseric, kirillgroshkov, and 39 more reacted with eyes emoji
All reactions
-
👍
670 reactions
-
👎
20 reactions
-
🎉
161 reactions
-
❤️
261 reactions
-
🚀
190 reactions
-
👀
49 reactions
|
Review requested:
- @nodejs/actions
- @nodejs/gyp
- @nodejs/loaders
- @nodejs/security-wg
- @nodejs/tsc
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
legendecas
left a comment
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 thank you for the great work!
I think correct source locations in the error stacks are required in the short term as well.
One possible solution could be that avoid altering source locations in stripping so that source maps generation and mapping would not be required. Note that source maps come with a cost and it would not be trivial (per memory usage and performance). (idea from @acutmore)
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
imranbarbhuiya, SukkaW, jondubois, mubaidr, spikebrehm, yorkie, Sujiuzii, matthiasfeist, gabssnake, davilima6, and 2 more reacted with thumbs up emoji
marco-ippolito, SukkaW, kirillgroshkov, Necrelox, jondubois, and jochongs reacted with heart emoji
All reactions
-
👍
12 reactions
-
❤️
6 reactions
Uh oh!
There was an error while loading. Please reload this page.
🎉 thank you for the great work!
I think correct source locations in the error stacks are required in the short term as well.
One possible solution could be that avoid altering source locations in stripping so that source maps generation and mapping would not be required. Note that source maps come with a cost and it would not be trivial.
Sourcemaps are already supported by swc and we might just add a flag to enabled them.
We could ask swc an option to replace syntax that have been removed with whitespaces so that we don't alter original error stacks
|
mubaidr, matthiasfeist, gabssnake, and davilima6 reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Master-Hash, rla, and gabssnake reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as resolved.
panva
commented
Jul 4, 2024
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
@marco-ippolito, speaking as a module maintainer here, it is very common for TS module codebases to have its own file imports written with .js extensions in the import statements, despite the files being .ts.
If I'm to replace --import=tsx/esm with --experimental-strip-types, or skip a compile step before testing, this would need to be able to resolve to a .ts file even when .js was in the statement (but not in the filesystem), otherwise I'll have to change the project imports and then deal with more pain from the compiler (or tsc typecheck) before emitting the actual module code to publish.
|
wojtekmaj, niieani, karlhorky, krzkaczor, kirillgroshkov, everett1992, robbiespeed, octet-stream, Master-Hash, apcjs, and 20 more reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
@marco-ippolito, speaking as a module maintainer here, it is very common for TS module codebases to have its own file imports written with .js extensions in the import statements, despite the files being .ts.
If I'm to replace --import=tsx/esm with --experimental-strip-types, or skip a compile step before testing, this would need to be able to resolve to a .ts file even when .js was in the statement (but not in the filesystem), otherwise I'll have to change the project imports and then deal with more pain from the compiler before emitting the actual module code to publish.
This is something we definitely want look into, to be able to maintain compatibility with what you run and what you compile.
It's in the checklist for future iterations, it might be resolved with extensions guessing (?), it requires further discussion.
|
panva, niieani, jazzypants1989, JannicBeck, Conaclos, mubaidr, Wolven531, and gustavopch reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This is something we definitely want look into
Glad it's on your mind then!
As a sidenote, would --strip-types be a no-op if https://github.com/tc39/proposal-type-annotations ever came to be?
It's still in Stage 1 so it will take at least a few years but I hope v8 will implement the api to make it happen 🔥
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Resolution of import specifiers isn’t specific to module type, so we can’t have extension searching just for TypeScript files; and for many reasons we don’t support it in ESM. However it’s not necessary: users should write their import specifiers with .ts extensions, like import './file.ts', and there’s https://www.typescriptlang.org/tsconfig/#allowImportingTsExtensions allowImportingTsExtensions to support this via tsconfig.json. Such behavior also aligns us with Deno.
If the user wants to use the same source files both run directly via this new flag and run as transpiled JavaScript output by a build tool, it would be the responsibility of the build tool to convert file.ts into file.js; but this is something that build tools are more than capable of handling.
|
gabssnake reacted with thumbs up emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
There are two aspects of TypeScript support here:
- Stripping the syntax (i.e. essentially polyfilling the type annotation as syntax proposal)
- Modify the resolution rules so that
.ts files are accepted, with 1 applied (and as @panva mentioned, allow e.g. import 'foo' to resolve to /path/to/foo/something.ts)
AFAICT this does both (although the second one does it in a limited way), so that brings up the question on whether we want to make this this unflagged eventually. If we want to unflag it eventually then 2 is going to be a breaking change for existing TypeScript loaders that also try to modify the resolution rules in their own way, then we should only strip the syntax for files with explicit formats but don't try to override the resolution rules. If we want to keep it flagged as --strip-syntax eventually, then doing 2 is fine, though we might want to warn that it would be in conflict with other TypeScript loaders that actually do more like supporting enums.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
There are two aspects of TypeScript support here:
- Stripping the syntax (i.e. essentially polyfilling the type annotation as syntax proposal)
- Modify the resolution rules so that
.ts files are accepted, with 1 applied (and as @panva mentioned, allow e.g. import 'foo' to resolve to /path/to/foo/something.ts)
AFAICT this does both (although the second one does it in a limited way), so that brings up the question on whether we want to make this this unflagged eventually. If we want to unflag it eventually then 2 is going to be a breaking change for existing TypeScript loaders that also try to modify the resolution rules in their own way, then we should only strip the syntax for files with explicit formats but don't try to override the resolution rules. If we want to keep it flagged as --strip-syntax eventually, then doing 2 is fine, though we might want to warn that it would be in conflict with other TypeScript loaders that actually do more like supporting enums.
import foo from "./foo" is not supported, file extension is needed.
With this pr there is no resolution override, the test 👇
https://github.com/nodejs/node/pull/53725/files#diff-2fcd423c995b0d2501236a6f427092c5ab1df06ba9241285a471eebc2f12970dR40
I added an item in the issue to track the work to discuss about this
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
|
In test suite I think we should add some test to know how node will work with that:
and
import type { Config } from 'myConfig.d.ts';
export const config = {} satisfies Config;
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is neat, I'd like to see extensionless imports work if possible, but I also think this can land as-is and those issues fixed before making it stable.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
egasimus and GabrielBB reacted with thumbs up emoji
mashaal and gabssnake reacted with thumbs down emoji
All reactions
-
👍
2 reactions
-
👎
2 reactions
|
I see few issues with the module approach:
-
Usage of .ts extensions in imports and the allowImportingTsExtensions flag in TypeScript land is virtually non existent. Imports are either extensionless or use .js one (see point 2).
-
To complicate things even further ESMs resolution in TypeScript is mostly achieved in codebases by adding .js extension to imports even if the .js file at that path does not exist.
https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution-for-libraries
This is especially important in library code (as many projects out of there simply opt-out of ESM entirely and either create bundles FE-wise, or they compile down to CJS for Node ones) but the number of real world products using this pattern increases consistently every day with CJS being phased out by an ever growing number of projects.
I wonder whether the flag could strip types and run @swc/wasm-typescript on files regardless of their extension. If it is a normal js file it will simply not need to strip anything (albeit it will still have to parse it).
|
karlhorky, fregante, octet-stream, janmeier, aantia, egasimus, martin-juul, juxuanu, electrovir, haltcase, and 5 more reacted with thumbs up emoji
gabssnake reacted with thumbs down emoji
All reactions
-
👍
15 reactions
-
👎
1 reaction
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This is neat, I'd like to see extensionless imports work if possible, but I also think this can land as-is and those issues fixed before making it stable.
@benjamingr this is currently not possible because of this mandatory-file-extensions, it really depends on the esm loader.
Usage of .ts extensions in imports and the allowImportingTsExtensions flag in TypeScript land is virtually non existent. Imports are either extensionless or use .js one (see point 2).
To complicate things even further ESMs resolution in TypeScript is mostly achieved in codebases by adding .js extension to imports even if the .js file at that path does not exist.
@enricopolanski That is something that might be resolved with guessing, as you can immagine it requires node to perform a few expensive operation on the file system and a heuristic attempt to guess the extension.
Example:
import foo from './foo.js'
- stat
./foo.js => does not exists
- stat
./foo.ts => exists
And
import foo from './foo.ts'
- stat
./foo.ts => does not exists
- stat
./foo.js => exists
We will iterate on this and it's tracked in the issue nodejs/loaders#208
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This was referenced Apr 26, 2026
This was referenced Jun 30, 2026
This was referenced Jul 7, 2026
This was referenced Jul 28, 2026
This was referenced Aug 8, 2026
This was referenced Aug 17, 2026
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
Labels
commit-queue-squash
PRs the Commit Queue should land as one squashed commit.
esm
Issues and PRs related to the ECMAScript Modules implementation.
experimental
Issues and PRs related to experimental features.
lib / src
Issues and PRs involving general changes in the lib/ or src/ directories.
loaders-agenda
Issues and PRs to discuss during Loaders team meetings.
module
Issues and PRs related to the module subsystem.
needs-ci
PRs that need a full CI run.
notable-change
PRs with changes that should be highlighted in changelogs.
semver-minor
PRs that contain new features and should be released in the next minor version.
tsc-agenda
Issues and PRs to discuss during Technical Steering Committee meetings.
Milestone
No milestone
Development
Successfully merging this pull request may close these issues.
Uh oh!
There was an error while loading. Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
|