Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Zero-config TypeScript with Babel 7 #4146

Closed
jaredpalmer opened this issue Mar 12, 2018 · 23 comments · Fixed by #4837
Closed

RFC: Zero-config TypeScript with Babel 7 #4146

jaredpalmer opened this issue Mar 12, 2018 · 23 comments · Fixed by #4837

Comments

@jaredpalmer
Copy link
Contributor

jaredpalmer commented Mar 12, 2018

Zero-config TypeScript with Babel 7

As mentioned in several other issues, with the release of Babel 7 and CRA 2, TypeScript support is now possible in a way that does not dramatically change the core of CRA internals. Before proceeding with a PR, would love to gather feedback on a solution / implementation I came up with over the weekend:

Current Behavior

TypeScript users cannot use CRA directly. Popular alternatives include react-scripts-ts (a fork of CRA that uses the TypeScript compiler) and Microsoft's React TypeScript boilerplate.

Desired Behavior

CRA works with TypeScript via Babel 7 with zero-config. To enable TypeScript, users can just rename src/index.js to src/index.tsx.

Suggested Solution

  • Remove filename extension from paths.appIndexJs (i.e. src/index.js -> src/index) so it works regardless of .js or .ts or .tsx.
  • Add .ts and .tsx to the extensions in webpack config (resolve, babel loader, file-loader) and Jest config
  • Add a useTypescript flag (so that this choice is explicit) and related @babel/preset-typescript package to babel-preset-react-app
  • Use the filename extension of paths.appIndexJs to determine whether to set the useTypeScript flag in webpack babel-loader options and jest babel transformation
  • Add documentation about TypeScript usage

Areas of Interest/Discussion/Questions

  • Alternative ways to determine whether to enable TS?
  • Should tsconfig.json and tslint.json be zero config? (probably not)
  • Should we add built-in type checking with fork-ts-webpack-plugin? (my vote is against this)
  • (bonus) Add tslint-loader to Webpack config if in "TS mode"
    • Question: can we use eslint-loader with Babel 7 TS? (I'm not sure)
    • If not, we should add a minimal tslint-config-react-app package that is congruent with eslint-config-react-app

Suggested Documentation

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Check out this introduction to using static types in JavaScript if you are new to this concept.

Recent versions of TypeScript work with Create React App projects out of the box thanks to Babel 7. Babel 7 TypeScript does not allow some features of TypeScript such as constant enum and namespaces. You can read more about Babel TypeScript here.

To add TypeScript to a Create React App project, follow these steps:

  • Runnpm install --save typescript @types/react @types/react-dom (or yarn add typescript @types/react @types/react-dom).
  • Add "type-check": "tsc" to the scripts section of your package.json.
  • Add a "resolutions" section to your package.json to avoid conflicting types of React and ReactDOM in your dependencies:
"resolutions": {
    "**/@types/react": "16.0.40",
    "**/@types/react-dom": "16.0.4"
  },
  • Create a file called tsconfig.json in the root of your project:
{
  "compilerOptions": {
    "target": "esnext",                     /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
    "module": "commonjs",                   /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
    "jsx": "react",                          /* Use React to interpret JSX */
    "noEmit": true,                         /* Do not emit outputs. */
    "allowSyntheticDefaultImports": true    /* Allow default imports from modules with no default export. 
  }
}
  • Rename src/index.js to src/index.tsx

Now you can run npm run type-check (or yarn type-check) to check the files for type errors. You can optionally use an IDE like Nuclide or VSCode for a better integrated experience.

To learn more about TypeScript, check out its documentation.

@jaredpalmer jaredpalmer changed the title [2.0] Zero-config TypeScript with Babel 7 [RFC]: Zero-config TypeScript with Babel 7 Mar 12, 2018
@jaredpalmer jaredpalmer changed the title [RFC]: Zero-config TypeScript with Babel 7 RFC: Zero-config TypeScript with Babel 7 Mar 12, 2018
@jergason
Copy link

Thanks for thinking about this! I think it would be fantastic if CRA supported TypeScript. Some initial feedback as someone mucking around with trying to update react-scripts-ts to CRA 2.0.0 (https://github.com/jergason/create-react-app-typescript/tree/jergason/update-to-2.0)

ESLint Loader Stuff?

Question: can we use eslint-loader with Babel 7 TS? (I'm not sure)

I might be misunderstanding this. Are you asking if we can lint TypeScript files with eslint-loader?

What eslint parser would this use? I've been using eslint with https://github.com/eslint/typescript-eslint-parser and it has a few major issues. It parses TypeScript successfully, but it falsely flags interfaces and fields in interfaces as undefined variables.

Or are you just saying we'd lint the code after compiling it?

When To Type Check

You're suggesting running the compiler as a separate package.json script, which means the build wouldn't type check, right? This means anyone who wants to make sure their builds type check correctly needs to tweak their package.json more, which seems to go against the "zero config" philosophy. Flow support requires adding a "flow": "flow" script to your package.json and then running npm run flow init, but once it's on it just works with the build system IIRC.

Zero-config (or less config) tsconfig.json

As an alternative to including a sample tsconfig.json in the docs, if you add "type-check": "tsc" to your package.json scripts you can run npm run type-check --init to generate one. The default output doesn't match what you have here, but you can pass in options to tsc --init to customize it. Maybe we could just include the options to pass to the command in the readme?

Also, for parity with the way flow works, maybe it's better to name this field in package.json tsc?

"resolutions" section in package.json

I think this feature is yarn-only. Is there a way to make this work for npm users as well?

@jaredpalmer
Copy link
Contributor Author

Thanks for the reply @jergason. Been a while since React Rally!

ESLint Loader Stuff?

I'm not up on my eslint stuff cuz I work in TS all day. I'd rather use tslint, but was just wondering if there was a new eslint thing with Babel 7 TS that could make for less code to manage/PR smaller.

When to typecheck

Yes. My suggestion is that typechecking would happen in a separate process (and terminal window). I was just going for parity with Flow documentation and method of installation here. AFAIK flow does indeed need to be run in a separate process, but editor plugins pick up flow immediately and the same is true for typescript.

So to summarize:

  • fork-ts-checker-webpack-plugin: Technically runs in (a) separate process(es), but does so behind the scenes and outputs errors to the same terminal window that users run yarn start/build command in. I use this on my Razzle projects at the moment, and it works very well. However, to get the absolute best performance, it requires tweaking the number of workers it uses. Using too many actual decreases performance and slows down compiles in my experience. This is why I suggest not using it for CRA.
  • tsc -w: Leverage typescript's compiler just for typechecking as descibed (when used with the noEmit:true config flag. This would need to be run in a separate process and terminal window (like Flow).

Note that neither options impact how editor plugins interact with TypeScript. For example, VSCode language server will pick up and display TS and TSLint errors in the background on open files (without tsc -w running).

tsconfig.json

tsc --init requires typescript to be globally installed. It also outputs a mess of a file IMHO. My suggestion was to just make it as easy as possible to understand what is going on. I'm totally fine adding CLI options to curtail output if that's preferred over copypasta.

resolutions

I've only ever used this is to fix a TS bug regarding nested @types dependencies when using yarn--never thought of it as "feature." This bug does not impact npm users so this workaround is not needed.

@evertbouw
Copy link

That's quite a few steps to call this "zero config". People should probably still use --scripts-version=react-scripts-ts or something similar, let it add the extra config files but then add the regular react-scripts to the package.json

Or would it be possible to put these steps in a script like eject, maybe even let CRA run it automatically if it finds the useTypescript flag?

@jaredpalmer
Copy link
Contributor Author

@evertbouw I disagree. Furthermore as described above, this setup is almost identical to the effort / config required to setup Flow + plus changing filename.

@sebinsua
Copy link

sebinsua commented May 8, 2018

I really want this, too. Right now this project seems to default to supporting Facebook libraries like flow and graphql over what is most popular in the wild.

Obviously, there is already react-scripts-ts, but I personally think that TypeScript's tooling is a dead-end. Babel is a better integration point for stuff like babel-plugin-macros, various css-in-js plugins and optimizations. (I think this is likely to continue when people start to include stuff like prepack into their builds, too.)

There is already this (react-app-rewire-typescript-babel-preset) but I don't think it's a good idea to monkey-patch bits of config (as when they change, fixing it will involve modifying a function with some relationship to the config, instead of a more concrete object literal).

@codepunkt
Copy link

This is such an amazing idea and really well thought-out. Thanks for the work @jaredpalmer and @jergason!

Would love to see this. How can we help?

@BernieSumption
Copy link

I'd be happy to help develop and test this if it gets past the proposal stage. I would suggest adopting a "light touch" approach - just do the minimum to enable TS development, and let users configure TSLint if they want to, like the approach taken with Sass.

As for exposing tsconfig.json, I think you can probably hide it. Just enable all the various features like decorators. A good argument for hiding it is that usage with Babel requires "isolatedModules": true (or rather, if the flag is omitted then people can write code that will not function correctly with Babel).

@lookfirst
Copy link
Contributor

Babel 7 TypeScript does not allow some features of TypeScript such as constant enum and namespaces.

While I would love to see CRA support TypeScript out of the box, I personally don't agree with using a version of the typescript compiler that isn't compatible with the actual TypeScript compiler. I think that will cause a lot of confusion and brittleness over time.

@ianschmitz
Copy link
Contributor

We have TSLint support coming soon over at strothj/react-app-rewire-typescript-babel-preset#14.

I think this will be a great solution for those wanting TS support in CRA 2.0. Would be great to hear feedback on what the community would like to see regarding TSLint.

It's worth taking a look at the logic needed to wire in TS support.. there really wasn't much needed! My preference would be to have TS supported directly by CRA, but i understand the desire to keep things as simple as possible.

@reyronald
Copy link
Contributor

I've been very excitedly following this issue since the day it was opened, and want to thank @jaredpalmer for taking the first step in that direction by crafting this proposal. However, I haven't seen much attention from the maintainers and that makes me sad :(. It's understandable though, I know there are a lot of other priorities, but I think having TS support out of the box would be a HUGE benefit.

Is there anything we can do to push this forward a little? Maybe make a PR with a proof of concept? Since I'm so heavily interested in this I'm willing to contribute in any way possible.

/cc @gaearon


While I would love to see CRA support TypeScript out of the box, I personally don't agree with using a version of the typescript compiler that isn't compatible with the actual TypeScript compiler. I think that will cause a lot of confusion and brittleness over time.

I somewhat agree with @lookfirst 's statement here. I don't know how Babel 7 compiles TS and haven't looked into it, but I'm assuming that it doesn't use tsc under the hood given that there are some features from the language it doesn't support. Is there no simple way we could achieve it using the official transpiler instead of Babel ?

@rajington
Copy link

@reyronald What unsupported language features are you missing? What concerns do you have about using something like create-react-app-typescript?

It'd be hard to maintain CRA's painless dev experience if every TypeScript feature needs to work perfectly with everything else CRA offers. It'd likely increase the number of issues and integrations CRA has to worry about (importing images, for example).

Maybe we need to see something like CRA from Microsoft, but in the meantime they're highlighting this approach as well.

@ianschmitz
Copy link
Contributor

I somewhat agree with @lookfirst 's statement here. I don't know how Babel 7 compiles TS and haven't looked into it, but I'm assuming that it doesn't use tsc under the hood given that there are some features from the language it doesn't support. Is there no simple way we could achieve it using the official transpiler instead of Babel ?

Babel doesn't really compile the TS. It strips the types from the source leaving plain JS, and continues running the result through babel (like babel-preset-flow). There are some advantages to this such as that we can take full advantage of the babel ecosystem of plugins, like what CRA already has configured.

@lookfirst
Copy link
Contributor

@ianschmitz To be clear, my only comment applies to the idea that whatever standard TS that goes into Babel, should not cause Babel to fail during its transformation step. If it is just stripping things back to standard JS, then it sounds like there shouldn't be any issues. 👍 from me! =)

@sebinsua
Copy link

sebinsua commented Jul 9, 2018

Within my current workplace we're currently using a forked version of React Scripts which achieves the zero-config TypeScript setup described here. We've not had any serious problems with it so far.

The only problem we've had was that the output of fork-ts-checker-plugin was lost on the first execution (when in async mode), because of react-dev-utils doing an automatic clearConsole when the compilation is first finished.

I think perhaps you wouldn't want to use this plugin in create-react-app, however if you don't use it then a developer will need to rely on something else to run typechecks and lints in watch mode -- this is sometimes problematic, for example: last I checked tslint doesn't have a CLI watch mode.

In my fork TypeScript support is always switched on, however it could be changed so that the existence of a tsconfig.json at the root folder is used to decide whether to switch TypeScript support on or off.

@brunolemos
Copy link
Contributor

@sebinsua could you please share a link to the modified fork?

@reyronald
Copy link
Contributor

What concerns do you have about using something like create-react-app-typescript?

@rajington Just the fact that it's a separate project not officially maintained by Facebook and that needs a separate effort to keep in sync with react-scripts's progress. Take a look at this issue as an example: wmonk/create-react-app-typescript#333, that's a minor thing (although it's considered major for some). When I'm using create-react-app-typescript, I'm not 100% confident that the decisions taken in that project would be approved by the original authors of CRA.

Are you against of including official TS support in CRA ?

Babel doesn't really compile the TS. It strips the types from the source leaving plain JS, and continues running the result through babel (like babel-preset-flow). There are some advantages to this such as that we can take full advantage of the babel ecosystem of plugins, like what CRA already has configured.

@ianschmitz Thanks! Like I said, I didn't know how it worked. If that's how it is, I'm not very concerned by what I mentioned before. And I definitely prefer to stay in the Babel ecosystem because of what you just mentioned.


In general, I would like to know how the rest of the community feels about having TS support in CRA? I originally thought it was a no-brainer "yes", but seeing some of your concerns and comments it feels like not everybody thinks the same way, but it is not crystal clear to me if you are leaning on "yes" or "no". Especially because there's no PR open to move this forward!

@brunolemos
Copy link
Contributor

brunolemos commented Jul 29, 2018

I made the PR #4837 with TypeScript support using babel 7.
Pretty similar to what was proposed here.

@hieu-nv
Copy link

hieu-nv commented Oct 4, 2018

I have created a version of react-scripts to use TypeScript with zero configuration. Please try to use https://www.npmjs.com/package/@hieunv/react-scripts

@wiesson
Copy link

wiesson commented Oct 9, 2018

Can I use typescript without ejecting? It's not clear to me how CRA2 handles .babelrc for "@babel/typescript" preset.

@ianschmitz
Copy link
Contributor

@wiesson please see PR #4837 for more info of what is coming!

@Timer
Copy link
Contributor

Timer commented Oct 30, 2018

TypeScript is now officially supported as of Create React App 2.1. Read the release notes to get started!

@Vinnl
Copy link
Contributor

Vinnl commented Oct 30, 2018

For those coming from create-react-app-typescript, I've written a short guide on migrating to Create React App.

@harrysolovay
Copy link

@jaredpalmer I tried to monkeypatch the fork-ts-checker-webpack-plugin instance in webpack.config.dev––in the updated config, I assigned the tslint.json path to the options.tslint field (as specified in the docs)... I tried swapping the plugin with an entirely new instance as well. I toggled silent back and forth... I'm not sure where I'm going wrong or if this is even the right approach. Really hoping you have some insight into how I can solve this problem. If not for the strict standards of create-react-app's governance, how would you go about enabling custom TSLint configs?

@facebook facebook locked as resolved and limited conversation to collaborators Nov 28, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.