@devongovett If you had some time, can you please write the steps needed/files to be changed to enable TS support. Then I can try to do so too. Thanks đ
Since parcel already has babel support, you can try using babel-preset-typescript , currently in beta. Register the .ts
extension here and configure .babelrc
to use the typescript
preset.
Unfortunately babel-preset-typescript
isn't particularly useful in most cases as it only does a transform and doesn't actually compile using tsc
, so you'll never get any type checking (the main reason to use TypeScript) with it, just es5 output.
It seems the ideal solution to this issue would be for Parcel to support TypeScript natively as an opinionated decision like fusebox does, or for someone to implement a TypeScript plugin that passes .ts
/ .tsx
files through tsc
prior to the Babel step (so you still get your Babel polyfills etc. after tsc
has converted your TS to ES2015).
Personally I would prefer the former as I think it would be much cleaner to use and I feel the vast majority of the JavaScript community is already using TypeScript so this would be a desirable feature for a huge portion of your userbase.
I think TypeScript support should totally be built in. We may require that you install typescript
in your project so we don't depend on it unnecessarily similar to how we do it for stylus, sass, etc. But you shouldn't need to install an additional plugin beyond this.
I don't personally have any experience with typescript, so I'll need some input here. I like the idea of using babel-preset-typescript
if that works, since other babel plugins will work along side without needing to have typescript codegen and parse again with babel. Open questions:
Yeah I agree, having it as a peer-dependency would be ideal so that the typescript
version you use isn't tightly coupled to your parcel version.
tsc
provides a lot more than type checking, I just mentioned that as it's the primary feature. Using the babel preset would mean you miss out on all warnings from tsc
, for example code quality compiler options like allowUnreachableCode
, noImplicitAny
and so on would not be enforced. Generally developers would see these warnings in their editor, but I don't think that is something to be relied on.
Workflows for bundling a TypeScript project via Browserify, Webpack or FuseBox all utilize tsc
and will output errors / abort build if there are any tsc
warnings, I believe a lot of developers have relied on this in their CI (so their CI build fails if there's any TS warnings). A lot of people using stuff like webpack-dev-server
are using it instead of tsc -w
and so expect to see any warnings come up in their terminal / browser.
From what I can see the babel preset is basically just stripping all the TS stuff so you get plain JavaScript, going by this another potential solution could be to use the babel preset for transpiling, but call tsc --noEmit
(which will output any warnings and return an error exit code if there are any problems while never actually outputting any files) before the Babel step on projects that have a tsconfig.json
or typescript
in their package.json
. This would allow Parcel to transpile TS â JS using just the Babel plugin, while still aborting and providing warnings to TypeScript users if something is wrong.
ts-node
used a similar mechanism; they do a basic transpile (via TypeScript's transpileModule
rather than a Babel plugin), and does the TypeScript type checking as a separate process: https://github.com/TypeStrong/ts-node/issues/44
Some suggestions. I think it would be ideal to send TypeScript errors/warnings to the browser console instead of to the local machine console. Really, TypeScript errors are just type check warnings, and it feels like a more unified experience to have all of my client code errors and warnings appear in one place, the browser console. It would also be ideal to not force the compilation to fail if there are any TypeScript errors, because again, they aren't really errors, the code will often still run, and for development it is sometimes nice to ignore TypeScript warnings to move quickly and prototype, going back to put in the types after things settle a bit. Allowing these two options would be ideal, and if you need help I've got som experience implementing this in a similar project.
Worth noting is that Babel 7 will come with built in typescript support, so the problem might solve itself in the near future
@wyqydsyq VSCode, Webstorm and probably other editors support TypeScript language service for code validation and type-checking errors directly inside the editor. This means you can have a nice Typescript developer experience while improving the compiler performance, because as you said, Babel does not do any type-checking. I remember I saw some tweets about using Babel instead of tsc to improve build time. I need to check my sources and find some benchmarks though.
edit: here's the tweet I read https://twitter.com/jaredpalmer/status/936249988270706688
@GeeWee in planning for Babel 7
You can now use babel-preset-typescript to allow Babel to strip types
Wouldn't this bypass the type checking of tsc?
I think we should do both:
There's no way to guarantee that Babel and TypeScript are perfectly in sync with syntax support. So people should have the option to stick to the TypeScript compiler.
I'd suggest stating with Babel, and putting tsc on the backlog.
In the interest of keeping the noise down on this issue, please use the đ button on the original post or any comments.
@Olian04 yeah that's a good point, we'd want to fork and typecheck in a separate process, like e.g. ts-loader does, but in general not having the typechecker run 24/7 is not as big of a problem as one night think as the editors will autocomplete fine without.
Itâs worth keeping in mind that, even if you donât care about type-checking,tsc
is still a very different compiler than Babel. Case in point, there was recently an article bemoaning the output of a Babel for..of
loop. tsc
doesnât have this problem.
On top of that, Babel 7 still doesnât support some tsc
features (namespaces and const enums come to mind). Arguably there will always be mismatches, especially since TypeScript moves pretty fast.
Finally, tsc
is quite configurable, with many compiler options which are not type-related, but change the behavior or output of the compiler. Unless Babel supports all of these, switching a TypeScript codebase to a Babel-powered Parcel will break things, which will reflect poorly on Parcel, IMO.
Performance-wise, according to a Twitter thread linked here earlier, tsc
in transpileOnly
-mode is pretty much as fast as Babel. I never had issues with tsc
performance.
Also, in my experience the TypeScript compiler has been very nice to work with as an embedded library.
Interestingly the first issue ever in TypeStrong's discussions repo is about normalizing non-standard tsc
compiler options from different libraries, if the transpileOnly
option from ts-loader
mentioned there were to be adapted as a standard option, Parcel could run tsc --transpileOnly
and tsc --noEmit
in separate threads to get both fast transpilation and TypeScript warning feedback without having to implement anything bespoke
Cool, sounds like we should go with the regular TypeScript compiler for compatibility by default. I think we can start with transpileModule
for now to unblock people quickly, and maybe add type checking later.
One unfortunate thing about this vs using babel is that it will probably require multiple parses/codegens, which I was trying to avoid for performance. We don't really want to reimplement all of the dependency analysis and other things that are currently implemented on top of the babel AST though. That means that TypeScript will parse and compile, and then babel will do the same. This isn't any worse that other bundlers currently, but a pure babel pipeline would avoid that.
Anyway, here's how someone might go about doing this:
You'll need to add a new Asset type. I'd inherit from JSAsset
so you get the rest of the analysis for free. Basically you need to wrap the parse function so it calls typescript first.
Here's some pseudocode.
const Asset = require('../Asset');
const localRequire = require('../utils/localRequire');
class TypescriptAsset extends JSAsset {
async parse(code) {
// require typescript, installed locally in the app
let typescript = localRequire('typescript', this.name);
// call typescript compiler
super(transpileModule(code));
}
}
Would someone like to take this on? đ
@d4rkr00t Maybe you want to take this on? đ
I would love to, but seems like it's a little bit too late :)
EDIT: Ignore me, I was testing the wrong version. 1.1.0 is not in NPM yet.
@devongovett I know this issue is already closed but I just found a bug. If the script index file has a .ts
extension, the mime
module, (transitive dependency of serveStatic) will interpret it as a "video/mp2t" (cf https://raw.githubusercontent.com/broofa/node-mime/master/types/standard.json).
My intuition is that the transpiled file should not have the .ts
extension anyway since it is not typescript at this stage anymore but we could also override the default mime type to map ts
to application/javascript
content type.
TypeScript has neat and not-complex tsconfig.json for driving/tweaking compilation. Ideally that's supported automatically out of the box.
TypeScript without tsconfig.json
by default just bulds every .ts file it can find in directory/subdirectory with reasonable default settings.
That's what should be the baseline default expectation.
@mihailik the tsconfig compiler options are used by the compiler, in this base implementation?
On the versions: it would be FANTASTIC if parcel could build TypeScript out-of-the-box, without extra step of installing another package.
But if a project wants to pick a specific version, that should be used instead.
That sounds like a trivial setup: make parcel-bundler
depend on typescript
but in non-comittal version like >=1.0.0 or something.
Yes @DeMoorJasper , tsc.js
(which is TypeScript compiler) takes in account tsconfig.json
if it exists in top directory and run without overriding --project myothertsconfig.json
argument.
The reason local requires are used is because of package size and speed, lots of people don't need typescript to be installed so it's not required by Parcel but if they want to have it they just install the official typescript package and they have it without even touching any config file.
This is the same for every non-standard language in here (less, stylus, ...)
There is also a plugin for advanced typescript support including typechecking and all the other fancy things
You're getting me kinda confused about config files here, tsconfig files in parcel are loaded the same way we handle the babel config or any other config. @mihailik
let tsconfig = await config.load(this.name, ['tsconfig.json']);
// Overwrite default if config is found
if (tsconfig) transpilerOptions.compilerOptions = tsconfig.compilerOptions;
transpilerOptions.compilerOptions.noEmit = false;
@DeMoorJasper quite reasonable on the size.
However TypeScript actualy only needs several files: tsc.js
and several lib*.d.ts
declarations. The rest is international language translations, IDE integration support and such. Those few required files take around 7Mb in current TS version.
For context: I've been following and contributing a few small bits to TypeScript since early, concerning compatibility and unusual integration scenarios. And totally agree with parcel's concerns in bundlers like webpack and browserify - so I'd love to help on TS integration, in whatever way fits with project's vision.
@mihailik Feel free to open up a new RFC in issues with the idea's you have to improve Typescript support within parcel, i'm happy to help improve the typescript support in any way.
@mihailik I'm not following your suggestion to use defaults. Are you suggesting that tsconfig.json
should not be supported by Parcel? I, for one, have hundreds of .ts
files and do not want them compiled in place by tsc
to seperate .js
files. They end up getting commited/checked-in by other developers and causing all kinds of merge problems. I would love to see a tsconfig.json
file supported out of the box by Parcel.
TypeScript support seems to work pretty fine already.
But I'm not sure how to get Polyfills?
I'd love to just add a .browserslistrc
and have it work.
Is it possible yet or should I move to Webpack for now?
__EDIT:__
I ended up using https://github.com/ryanelian/ts-polyfill and applying the correct libs
configuration in tsconfig.json
.
I'm quite happy with this solution. It doesn't add that much overhead. I'm still using the original TypeScript compiler. If I'm about to use a feature that's missing PHPStorm Inspector will error on me.
For me, TS compilation works fine, but for some reason, I can't see any errors/warnings from the compilation process. Does parcel intentionally swallow error messages? Is there any way to change that configuration?
Thank you for explaining.
@csillag i guess it's because parcel
don't use tsc
for compilation *.ts files at all (it's transpiled by babel
which don't care about type-checks, etc.).
Most helpful comment
Itâs worth keeping in mind that, even if you donât care about type-checking,
tsc
is still a very different compiler than Babel. Case in point, there was recently an article bemoaning the output of a Babelfor..of
loop.tsc
doesnât have this problem.On top of that, Babel 7 still doesnât support some
tsc
features (namespaces and const enums come to mind). Arguably there will always be mismatches, especially since TypeScript moves pretty fast.Finally,
tsc
is quite configurable, with many compiler options which are not type-related, but change the behavior or output of the compiler. Unless Babel supports all of these, switching a TypeScript codebase to a Babel-powered Parcel will break things, which will reflect poorly on Parcel, IMO.Performance-wise, according to a Twitter thread linked here earlier,
tsc
intranspileOnly
-mode is pretty much as fast as Babel. I never had issues withtsc
performance.