maybe it is common, I can't speak to that but in my opinion a large part of the success of webpack was probably because they bundled the typechecking. Because that's the only workflow that makes sense, imagine a c# compiler that quickly outputs executable IL code but half the time it's broken because it didn't do any type checking and you have to wait for the IDE based type checker anyway. On every build. It just doesn't make sense to work this way, you never want a fast, silently broken build which is what you get with non-typechecked fast builds.
The typescript plugin that you used may have included type checking, but it very much depends on how you set it up. Many common setups use Babel to compile typescript code, which doesn't do any type checking at all, just mechanically strips away the type signatures. I would be very surprised if much of webpack's success has come from the type checking functionality you describe.
As for whether it's "the only workflow that makes sense", I find the most comfortable flow for me is using the compiler as a linter rather than a "true" compiler. It runs in my IDE, potentially as a pre-commit script, and in CI (which means checked in code cannot fail to type check), but I can still build failing code. This would be, as you say strange if I were using C#, but syntactically valid typescript can always be covered to syntactically valid javascript, so the output won't be "broken" in the same way that a C# program with invalid types would be. Most of the time, a program that isn't accepted by TSC is still a valid javascript program, just with poorly specified input and output types.
That said, while it can be useful occasionally if I'm still trying to figure out the types, the advantage here is less that I can now compile invalid code, and more that my bundler doesn't need to do unnecessary type checking. Any errors I can catch myself based on the red squiggles in my editor, so I know not to look at the browser until I've fixed all the issues, and at that point I don't need my bundler to also confirm that the types are accurate. Each tool does one task well. (Similarly, I don't want my bundler to also run all the unit tests before it compiles, even though I would normally expect the tests to be passing before I check the output of my code. 90% of the time, if I run the bundler, my tests are passing, and in the last 10%, I probably want to quickly try something out without changing the tests. The same logic usually applies to my types.)
As a result, I strongly disagree with the claim that your process is the only one that makes sense. It may be the most logical for you and your team, but I tend to find that it makes sense to approach typescript from a different angle to most typical compiled languages, and I've found a lot of success in my process.
How is it not a problem for you that you make a change in a .ts file and then you are immediately in a world of uncertainty: Is the typecheck already complete? No clear progress on this. The js output is probably already here, but not sure unless you constantly want to check yet another terminal window. So can I reload the page yet? Oh no wait, a few more type errors popped up after all, so I can't yet. This all happens in under 2 seconds but those 2 seconds are uncertainty and slow you down far more than any wait for a normal sized typescript project where typechecking is done integrated with the build.
I don't think that's it. I write pretty much exclusively typed code, except in places where I've not managed to get everything set up properly (mainly old Vue2 code). And even then, imports are unidirectional: JS can import TS, but TS can only import other TS files.
As to the "world of uncertainty", this lasts surely less than 100ms for me, certainly little enough time that it feels instantaneous. Usually I get feedback as I'm typing, sometimes if I'm doing things in new files I need to save the file first, but either way, my IDE is consistently fast enough that I know immediately if my code type checks or not. There is no downtime here at all.
As for seeing the code change, I usually have the page open on a second monitor, so I press "save", and glance over to my second monitor, and with live reload and a good configuration (that's harder to achieve on some projects, I grant you), I'm usually already looking at the results of my changes.
In practice, I very rarely have any uncertainty about what typescript thinks my types are - this information is always immediately at my fingertips. Where I do have uncertainty sometimes is whether the javascript types match the expected typescript ones - this often happens when I'm dealing with third-party libraries, particularly when `any` types start popping up. In that case, it's occasionally useful to ignore the type checker altogether, play around with some different cases, and then come back to ensuring that the types match later. That's just not really possible if you're treating the type checker and the compiler as an all-in-one unit.
You're right in saying that having a clean and understandable development environment has a lot of complicated moving parts! Dropping the ball on any one of them means having a bad developer experience.
Since I've said this on other comments, I'm not going to dwell on the fact that this behavior isn't something Webpack itself solves. However, if you make a change in a .ts file, two things typically happen in a dev server:
1. Your code is retranspiled and rebundled, usually using partial recompilation. 2. Your IDE sends these updates to the TS language server that powers your IDE's TypeScript support. That server uses the same typechecking logic as the TypeScript compiler, so it's a good source of truth for surfacing type errors in the files open in your IDE.
Depending on your dev environment, one of a few things might happen next: 1. Your dev server might hot reload the changes in your browser once the build is finished. 2. Your dev server might also block reloading if a type error is found. 3. Your dev server might even show you that error in your browser so that you know what's going on.
Next.js currently does all three of those things, which makes for a really nice developer experience. You don't really have to keep an eye on another terminal — everything shows up in your browser. They even render a little loading icon to let you know when a rebuild is taking place.
Next.js uses Webpack internally, but Webpack isn't the reason for this nice developer experience; it's just a core part of it, and is only responsible for bundling and rebuilding.
I love talking about this stuff, for what it's worth, so feel free to ask more questions. I helped Etsy adopt both Webpack and TypeScript when I worked there, so I have a good chunk of experience in the area.
I'm genuinely curious here. How do you ensure contract correctness? The whole point of static typing (or rather explicit typing) is to declare and enforce certain contract constraints prior to writing [client] code, which prevents certain bugs.
What's the point of using type-safe language if you deliberately circumvent type safety? If you "fix type errors" by declaring things to be strings you do not get much more type safety from TS than plain JS.
Or am I hugely missing something here?
It helps to think of TS types as a guarantee of behavior and an encoding of intention, and of TS itself as a really smart linter. As long as you use my function according to these types, it'll behave as expected. If it doesn't, that's my problem and I'll fix it. If I say my function returns a string, you can use my function's return types as a string with the confidence that that's how I expected it to be used. TS will make sure that everything agrees with my type assertion, which removes the need for checking the types of parameters in tests, for example.
https://blog.johnnyreilly.com/2017/09/07/typescript-webpack-...
Exciting news about turbopack - the world is about to get much faster!
It's critical to run type checking outside of webpack. You should be able to execute code that does not type check correctly. It maximizes iteration speed and is one of TypeScript's key superpowers.
Awesome!
> but half the time it's broken because it didn't do any type checking and you have to wait for the IDE based type checker anyway. On every build.
Huh? The IDE checks your types as you type. Not on every build.
This works great and de-duplicates effort. I have an extra "check" script that runs all the linters including the Typescript checker that you can run before making a PR or production build (tells you the same thing as the IDE). I'm glad it doesn't block my development build because it takes many seconds (10-20) while without it you can get updates in far below one. That's a night and day difference.
> you never want a fast, silently broken build which is what you get with non-typechecked fast builds.
You're misinformed about what I want. I want fast builds. Decoupling linting from building is a great way to achieve that. I am yet to experience any problems with it.
If you want to type check on every build you can put that into your pipeline, but it will unnecessarily slow things down. I for one am very happy with fast builds with no downside.