RFC: Zero-config TypeScript with Babel 7

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.

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?

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.

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?

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

I want this, too.
There is 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 be more difficult since you will need to modify a function instead of a concrete object literal).
Obviously, there is react-scripts-ts too, 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 various optimizations.

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?

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).

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.

We have TSLint support coming soon over at https://github.com/strothj/react-app-rewire-typescript-babel-preset/pull/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.

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 ?

@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.

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.

@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! =)

Within my current workplace we're currently using a forked version of React Scripts which achieves the zero-config TypeScript setup described here.
The only problem I had was that the output of fork-ts-checker-plugin was lost on the first execution (when in async mode), because of an automatic clearConsole when the compilation is first finished. (I think perhaps you wouldn't want to use this in create-react-app, however then a developer will need to rely on something else to run type-checks and lints in watch mode.)

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

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: https://github.com/wmonk/create-react-app-typescript/issues/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!

Hey guys! I made the PR #4837 with TypeScript support using babel 7.
Please try it out and comment there how we could improve it.

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

Can I use typescript without ejecting?

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

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

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

@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?