'.js' files in TypeScript - why?!
HTML-код
- Опубликовано: 28 июн 2024
- 00:00 The Problem
00:56 The Solution
01:20 Node Wants .js?
02:08 Why Not .ts?
02:41 What If I Don't Want .js?
Article: www.totaltypescript.com/relat...
Become a TypeScript Wizard with my free beginners TypeScript Course:
www.totaltypescript.com/tutor...
Follow Matt on Twitter
/ mattpocockuk
Join the Discord:
mattpocock.com/discord - Развлечения
I’m surprised that the TS team don’t let you simply import .ts and then have TSC compile the imports out as .js. Guessing they have their reasons though
Yeah. This going to lead to endless migrations back and forth. Merging with a team that uses a bundler? Time to change all your imports. Extracting some code out to a re-usable library where bundling is deferred? Time to change all your imports back.
@@steamer2k319 No, the `.js` extension is supported by bundlers. So there is no need to do this.
Their long term game here might be to phase out the tsc compiler
Yeah, thinking of compiled code feels so 2000s…
Atleast two reasons why;
The TypeScript team has stated that they don't want to rewrite your JS code, only remove TS type information (and even that they want to avoid, with the "types as comments" proposal). While it's true that non-const enum already does this, it's actually just a remnant from legacy TS to make it backwards compatible before they decided against rewriting JS (apart from bundling it against your target runtime).
It would also have additional impact on transpilation times; aside from just transforming the extension, it would also have to verify that this path actually exists (requiring I/O to check if file exists) which is slow. Without this vertification, it would be unintuitive if TS happily transformed module paths, and result in runtime errors. You'd also have little to no control over _how_ this transformation is done, adding a ton of headaches-you really don't want to debug a transpiler's implementation, only your own code.
I fully understand the argument why TS decided to go with it. But if I was on that meeting, I would have voted againt it.
I am down with adding `.ts` extension but `.js` extension for `.ts` file is just too counterintuitive for me & confusing. The transpiler should handle that or we can always stay in bundler moduleResolution.
It feels like adding .o to c code.. I am using TS as a compiler, I do not want to know what happens inside
"or we can always stay in bundler moduleResolution" yep, that's what it is for...
@@BarbaRuiva5 using tsc should already be a sign that you are doing something wrong
Perfect example of an abstraction leak.
This is one of those things that makes interop a nightmare. You could literally get stuck or have to change the way you import everithing because you added one build step or tool
It's actually more compliant with standard JavaScript to add `.js`, any modern tool should be able to handle this.
@@jonkoops I don't think so. It's called "source code" for a reason, but it's an opinion
This is literally the thing preventing me from updating to esm
One of the reasons why I love Deno is that you don't need to worry about the Typescript Configuration :D
Yeah I'm starting to see the appeal
We truly live in the worst timeline for Typescript.
AFAIK, another option is to use Node 20 - it should support imports without extensions. Maybe a bit longer startup times, but that is seriously not a problem for me.
I always loved the extensionless imports. When a file grew too big, I changed it into a directory with an index file, and none of the existing code would be affected.
Since it's not LTS yet, I haven't tried it yet, so I may be mistaken ...
(until mocha watch mode supports ESM modules, I stick with commonjs modules. The number one important time to measure is not startup time, but feedback time from I save a file until relevant unit tests are run. Currently that is typescript compiler in watch mode, and mocha in watch mode, monitoring the ts build output)
These kind of issues and disagreements make me want to write TS less, as the time goes by
This is all because TS team paranoia of just being a type checker and nothing else...
Really getting sick of it, we would have so many great things if it wouldn't be because of that hardcore limit.
Come on... developer experience is more important than Node internals experience
I'm sorry but I completely disagree with you. You saying it kind of makes sense because your compiled code is in .js files and it will consume those is just beyond the point. As you're saying, it's the *compiled* code we are talking about. You don't reference the compiled code from your Typescript files, but you reference the uncompiled Typescript code. They should simply adjust the file extension from .ts to .js in the compilation step.
Yeah I'm down with file extensions, but there is no amount of mental gymnastics that can make using .js instead of just having the ts compiler change it is anything but silly
My main issue with ".js" imports (even though I consider them more correct) was the lack of support in WebStorm IDE. Whenever I copy-pasted some thingy, the IDE was auto-generating an import, but it was with ".ts" extension (or extension-less at all, depending on the project config)
I think next we should target .bin extensions instead of .js and move straight to machine compiled version of the code that would make much more sense than simply stopping at the .js part, why not go all the way it also makes sense 😂
Or maybe byte-code, lol. There have been proposals for a stable BC spec, but such standardization will force all JS engines (V8, SpiderMonkey, ...) to have extremely similar BC implementations (which would make their performance similar) and it would prevent them from updating it internally (because stability requires retro-compatibility). That's how WAsm was born.
Funnily enough, there are already standalone WAsm runtimes similar to Node/Deno, so your idea is almost reality
Was in dare need of this explanation! Thank you, Matt!
sadly this is the reason, I still target commonjs when transpiling using tsc
I'm sorry, I don't get it, compiler is made to traverse us to the other side with dry feet, at the moment of compilation you NEED to have that file at THIS destination, so compiler will know what you meant when referring to .ts file and should automatically convert it to a valid .js import - simple as that. It's like you would be writing something in a native language and was wondering how the linker is gonna load/link your dll/so/whatever files - if you are not doing something fancy you don't care, you just import/include your source file or reference an external library and forget that linker even exists.
I sadly worked all this out by myself.. I am not usually one for hand holding, but I feel something like this would have been nice to have had Typescript hold my hand a little more...
Super informative, Matt. Thank you!
Surely having a compiler means you shouldn’t have to think about the structure/files of the compiled output? I’m curious why TSC can’t figure out which files you’re talking about and add the right extensions later..
Im pretty sure tsc can but thing tsc doesn't want to do anything that could be considered changing the code.
Or... instead of node, use bun, import without extension and have it run much faster... It's not about the speed, it's about two different team epeen contest (ts and node), going back and forth and not wanting to fix this.
bun is bleeding edge
If your goal is getting esmodules from TS, the easiest workaround for now that I’ve found is to just use esbuild for all bundling/transpilation, having tsc only do type checking
I desperately needed a video about this
And that folks is why Deno is better :)
I get why it wants to work like this given how typescript is structured, I'm just very confused why TS is so hell-bent on not changing your code. Sometimes it feels like you could "compile" TS to JS with a single `code.replace(/very long regex/g, "")` call
They (and I) see that as a benefit - a thinner layer is easier to conceptualize and remove.
@@mattpocockuk that is true, but at the same point it makes it considerably less powerful and I'm not just talking about how you have to put the extension there. This same limitation stops operator overloading from being implemented and makes overloaded functions much less useful.
I wish you could have those features without giving up the comfort of working on the web by using a native language compiled to wasm
We could have runtime types by now id typescript team would reconsider their goals. Hope soon we can see a bun equivalent to typescript, just so there is some competition.
Nope. This is stupid. In no other language do we have this problem. My kotlin code is compiled to any number of .class files, do I have to pre-empt where those files will be and what they'll be called? No. My python code is compiled to .pyc files, do I have to import the .pyc files? No.
💯
It’s because ES modules are designed to also work on the web. You don’t want the browser to do a lot of roundtrips just to guess where the actual file is located on the server.
As someone who eventually has to bundle my code to be ran on the browser, do I use the bundler resolution, have compiled bundles as es6 or later, then either use as is or transpile to es5 (for legacy purposes)?
you looked nice in the TS documentary btw
In Deno, since you can import a JS file into a TS file and vice versa, you have to use the actual file extension, which is much better.
My output extensions are a bundlers concern.
Especially because I'm building for both esm and cjs.
So, I use Rollup and all of "moduleResolution": "Bundler", "allowImportingTsExtensions": true, "noEmit": true.
(and rollup still issues a warning about allowImportingTsExtensions, even despite it says right there it is fine with noEmit)
Caught me off guard that I have to use extensions for relative imports, but not for external dependency imports
I’m all for making things faster, but feels like this could be a vscode extension to auto add the file extension. Pay the price once on save and never again. 99% of the time it’s easy to guess the extension and the rest can be left as errors.
TypeScript does this automatically - it's built in to VSCode. Also works with auto imports and moving files around.
Great
Deno mandated the explicit use of .ts extension in imports for getgo for exactly that reason.
We need to move away from JS asap, thing will just keep on getting worse from here 😅
I like so much this approach because I like to know how it works under the wood. But many other dev don't really want to know and are concerned about the way it looks
IIRC the reason typescript doesn't allow .ts imports is that they compile one file at a time.
Deno made .ts imports work by hacking into how typescript works.
How To Confuse Your Jr Dev (or Dev in General)?
This become unbearable when you use the .mjs or .cjs extensions, tsc do not allow those
Yes it does - you can use .mts or .cts extensions and use moduleResolution: NodeNext.
www.totaltypescript.com/concepts/mjs-cjs-mts-and-cts-extensions
Make video on ark types please
I think I finally got it.
- you use tsc to transpile your .ts files into .js files and put them into the folder called dist.
- you then decide to execute your main ts file with a tool like tsx that doesn't require you to transpile .ts files into .js files first to understand it.
- tsx sees the foo.js import and goes straight to /dist/foo.js to execute it as a js file. If the imported file had been .ts instead, tsx would have to first transpile the file into js and then execute it.
If this is actually what it is for, my question is, why not execute the transpiled js file with node rather than tsx if you already have access to the transpiled code?
tsx runs on source files. node runs on transpiled JavaScript.
I'm still lost lol. if it's not for tsx then what is it for? what tool benefits from js imports in ts files
@@flammea_ Node. You compile this code to .js using tsc, then Node can run it.
@@mattpocockuk This comment of @flammea_ illustrate perfectly why so many dev are frustrated. They think TS run the code. It looks so basic but you should consider to make a video to explain "You compile this code to .js using tsc, then Node can run it."
I guess many dev don't make the difference between writing code and executing code. That's why they don't understand why .js is needed here
0:50 This is like The Password Game, lol
This is why Bun > Node.
I'm kidding. 😜
Am I kidding? 😏
This is a mistake that they will regret and backout at a later time.
Exactly. Like .mjs which noone uses. They are losing the grip with reality.
What a ridiculous change! I get the reasoning and reckon it’s a load of codswallop. There was nothing wrong with the way it was working, but now you have to import a file that doesn’t exist? Just because … well, for no particular reason at all! So, so ridiculous.
Unfortunately this doesn't explain why we can't import `.ts`, then, when file is compiled, TypeScript wouldn't change that to `.js`. Compiler changes extensions of files it creates, so why can't it change extensions in imports?
I'd rather:
- Add bunch of exports in package.json
- Not use TypeScript at all
than add `.js` to imports, which points to files that do not exist. Absolutely counterintuitive nonsense from TypeScript team.
Another reason to use esbuild I guess
And to make it more confusing - if you're only importing types, then a no extension import will still work
This feels like a dumb move. If anything we should be using `.ts` and the compiler should convert them to `.js` in the output.
Thanks, Satan. I can't wait anymore, i need native type support in JS, this is so stupid.
The "s" sounds (ie. sibilance) in the video's audio is too sharp. Hurts my ears when listening :(
Might wanna do some de-essing on future audio
I will never use NodeNext due to this
Do you import compiled .class files in java
Or import .pyc in python?
Binary files in C, zig or rust?
Or similar story with, elixir, groovy which are transpiled.
Why does typescript think they are any smarter than all the existing languages and make it painful for all the Devs. 🤦♂️🤦♂️
Well... Actually, I think is better:
tsConfig: {
allowImportingTsExtensions: true,
noEmit:true,
moduleResolution: NodeNext
}
And use xwtsc to build or run
IMO it seems that Typescript innovation is coming to an end, so we better keep fighting for nitpicking situations to stay relevant... 🙄
TS is the tail wagging the dog
bun
Not using file extensions was dumb, rhe whole index.js thing was dumb. Using .js instead of .ts is also rrally dumb. It literally has a transpolation step. Just change it there lol
I dunno how I feel about this. :/ Doesn't Typescript transpiles to Javascript anyway even with NodeNext? Boottime should be exactly the same with all options.
This will go down in history as one of those insanely stupid decisions that'll eventually be undone. With everything typescript is doing to the code, the least it could do is replace a `t` with a `j`. Give me a break
TypeScript NEVER changes runtime behaviour. So, doing this would break a key rule.
But I sort of agree - TypeScript kind of 'owns' the .ts extension, so it makes sense to me that it would change .ts to .js.
Doesn't bother me as long as auto-imports put the correct thing. If it doesn't, that will be really annoying and I'll have a reason to be pissed off.
Yep, auto-imports just work.
.js is the best because it makes it possible to share code between node and browser environments (obv when node specific functions not included)
I'm surprised of how much people don't know the standards (not referring to Matt) until these days, but this is how esmodules works
Yep, it's just the bundlers obscured it for so long.
Standarts don't6 work with ts. Compiler should compile to standarts. It's an awful idea to forse writing this.
@@QwDragon I don't think they should. I actually think that keeping .js is the right thing to do, because the end code is js and the standard is for js not ts, let it turn ts to js is like hiding what you're actually working on. But it sure can have an option to do it if someone prefers
Solution: Use Bun
Nope thanks, I would never use .js extension on a ts file 🙅🚫
This whole thing is going to turn out to be a big mistake and get fixed later, in pretty sure of it
I'm sorry, but I don't care about performance enough to have to 1) write an extension to a LANGUAGE I AM NOT WRITING IN and 2) WTF AM I WRITING FILE EXTENSIONS ANYWAY? Seriously, how much of a performance decrease is automatically figuring out what file I'm talking about?!
Honestly, it can be pretty bad for serverless functions. Any call to the filesystem is extremely expensive performance-wise.
javascript is broken...
Seems unnecessarily harmful to the developer experience
I LOVE Javascript but its module system is the most dumb thing I’ve ever seen in any cose system..