Skip to content

Commit 88aef11

Browse files
brunolemosTimer
authored andcommitted
TypeScript syntax support (#4837)
1 parent 1ae90a1 commit 88aef11

22 files changed

+281
-40
lines changed

README.md

+1-3
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ Please refer to the [User Guide](https://facebook.github.io/create-react-app/doc
142142

143143
Your environment will have everything you need to build a modern single-page React app:
144144

145-
- React, JSX, ES6, and Flow syntax support.
145+
- React, JSX, ES6, TypeScript and Flow syntax support.
146146
- Language extras beyond ES6 like the object spread operator.
147147
- Autoprefixed CSS, so you don’t need `-webkit-` or other prefixes.
148148
- A fast interactive unit test runner with built-in support for coverage reporting.
@@ -175,8 +175,6 @@ Here’s a few common cases where you might want to try something else:
175175

176176
- If your website is **mostly static** (for example, a portfolio or a blog), consider using [Gatsby](https://www.gatsbyjs.org/) instead. Unlike Create React App, it pre-renders the website into HTML at the build time.
177177

178-
- If you want to use **TypeScript**, consider using [create-react-app-typescript](https://github.com/wmonk/create-react-app-typescript).
179-
180178
- Finally, if you need **more customization**, check out [Neutrino](https://neutrino.js.org/) and its [React preset](https://neutrino.js.org/packages/react/).
181179

182180
All of the above tools can work with little to no configuration.

docusaurus/docs/adding-typescript.md

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
id: adding-typescript
3+
title: Adding TypeScript
4+
---
5+
6+
TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.
7+
8+
Recent versions of [TypeScript](https://www.typescriptlang.org/) work with Create React App projects out of the box thanks to Babel 7. Beware that Babel 7 TypeScript does not allow some features of TypeScript such as constant enum and namespaces.
9+
10+
To add TypeScript to a Create React App project, follow these steps:
11+
12+
1. Run `npm install --save typescript fork-ts-checker-webpack-plugin @types/react @types/react-dom @types/jest` (or `yarn add typescript fork-ts-checker-webpack-plugin @types/react @types/react-dom @types/jest`).
13+
2. Rename the `.js` files you want to convert. Use `.tsx` if they use JSX or `.ts` if not (e.g. `git mv src/index.js src/index.tsx`).
14+
15+
3. Create a [`tsconfig.json` file](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html) at the root directory with the following content:
16+
17+
```json
18+
{
19+
"compilerOptions": {
20+
"target": "es5",
21+
"module": "esnext",
22+
"moduleResolution": "node",
23+
"lib": ["esnext", "dom", "dom.iterable"],
24+
"allowJs": true,
25+
"allowSyntheticDefaultImports": true,
26+
"esModuleInterop": true,
27+
"isolatedModules": true,
28+
"jsx": "preserve",
29+
"noEmit": true,
30+
"skipLibCheck": true,
31+
"strict": true
32+
},
33+
"include": ["src"]
34+
}
35+
```
36+
37+
4. Copy [loaders.d.ts](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/src/loaders.d.ts) from the template to your `src` directory.
38+
39+
Type errors will show up in the same console as the build one.
40+
41+
> Note: If you prefer to run type checking separately from the build process, you can run `npm uninstall fork-ts-checker-webpack-plugin` (or `yarn remove fork-ts-checker-webpack-plugin`) to remove the `fork-ts-checker-webpack-plugin` dependency and then `npx tsc -w` on a new terminal tab.
42+
43+
We recommend using [VSCode](https://code.visualstudio.com/) for a better integrated experience.
44+
45+
To learn more about TypeScript, check out [its documentation](https://www.typescriptlang.org/).

docusaurus/docs/running-tests.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ import Adapter from 'enzyme-adapter-react-16';
109109
configure({ adapter: new Adapter() });
110110
```
111111

112+
> Note: When using TypeScript with Babel, all your files need to have at least one export, otherwise you will get the error `Cannot compile namespaces when the '--isolatedModules' flag is provided.`. To fix this, you can add `export default undefined` to `src/setupTests.ts`.
113+
112114
> Note: Keep in mind that if you decide to "eject" before creating `src/setupTests.js`, the resulting `package.json` file won't contain any reference to it. [Read here](#initializing-test-environment) to learn how to add this after ejecting.
113115
114116
Now you can write a smoke test with it:
@@ -283,7 +285,7 @@ Example package.json:
283285
"name": "your-package",
284286
"jest": {
285287
"collectCoverageFrom": [
286-
"src/**/*.{js,jsx}",
288+
"src/**/*.{js,jsx,ts,tsx}",
287289
"!<rootDir>/node_modules/",
288290
"!<rootDir>/path/to/dir/"
289291
],

docusaurus/docs/setting-up-your-editor.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,14 @@ Next we add a 'lint-staged' field to the `package.json`, for example:
120120
// ...
121121
},
122122
+ "lint-staged": {
123-
+ "src/**/*.{js,jsx,json,css}": [
123+
+ "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
124124
+ "prettier --single-quote --write",
125125
+ "git add"
126126
+ ]
127127
+ },
128128
"scripts": {
129129
```
130130

131-
Now, whenever you make a commit, Prettier will format the changed files automatically. You can also run `./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx}"` to format your entire project for the first time.
131+
Now, whenever you make a commit, Prettier will format the changed files automatically. You can also run `./node_modules/.bin/prettier --single-quote --write "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}"` to format your entire project for the first time.
132132

133133
Next you might want to integrate Prettier in your favorite editor. Read the section on [Editor Integration](https://prettier.io/docs/en/editors.html) on the Prettier GitHub page.

docusaurus/docs/supported-browsers-features.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This project supports a superset of the latest JavaScript standard. In addition
1717
- [Object Rest/Spread Properties](https://github.com/tc39/proposal-object-rest-spread) (ES2018).
1818
- [Dynamic import()](https://github.com/tc39/proposal-dynamic-import) (stage 3 proposal)
1919
- [Class Fields and Static Properties](https://github.com/tc39/proposal-class-public-fields) (part of stage 3 proposal).
20-
- [JSX](https://facebook.github.io/react/docs/introducing-jsx.html) and [Flow](https://flow.org/) syntax.
20+
- [JSX](https://facebook.github.io/react/docs/introducing-jsx.html), [Flow](./adding-flow) and [TypeScript](./adding-typescript).
2121

2222
Learn more about [different proposal stages](https://babeljs.io/docs/plugins/#presets-stage-x-experimental-presets-).
2323

docusaurus/website/i18n/en.json

+3
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@
3636
"adding-relay": {
3737
"title": "Adding Relay"
3838
},
39+
"adding-typescript": {
40+
"title": "Adding TypeScript"
41+
},
3942
"advanced-configuration": {
4043
"title": "Advanced Configuration"
4144
},

docusaurus/website/sidebars.json

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
"using-global-variables",
3232
"adding-bootstrap",
3333
"adding-flow",
34+
"adding-typescript",
3435
"adding-relay",
3536
"adding-a-router",
3637
"adding-custom-environment-variables",

packages/babel-preset-react-app/README.md

+12-9
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,22 @@ Then create a file named `.babelrc` with following contents in the root folder o
3232

3333
This preset uses the `useBuiltIns` option with [transform-object-rest-spread](http://babeljs.io/docs/plugins/transform-object-rest-spread/) and [transform-react-jsx](http://babeljs.io/docs/plugins/transform-react-jsx/), which assumes that `Object.assign` is available or polyfilled.
3434

35-
## Usage with TypeScript
35+
## Usage with Flow
36+
37+
Flow is enabled by default. Make sure you have a `.flowconfig` file at the root directory. You can also use the `flow` option on `.babelrc`:
38+
39+
```
40+
{
41+
"presets": [["react-app", { "flow": true, "typescript": false }]]
42+
}
43+
```
3644

37-
To use this package with [`@babel/preset-typescript`](https://www.npmjs.com/package/@babel/preset-typescript), you need to disable `@babel/preset-flow` first.
45+
## Usage with TypeScript
3846

39-
You can achieve this by doing:
47+
TypeScript is enabled by default. Make sure you have a `tsconfig.json` file at the root directory. You can also use the `typescript` option on `.babelrc`:
4048

4149
```
4250
{
43-
"presets": [
44-
["react-app", {
45-
"flow": false
46-
}],
47-
"@babel/typescript"
48-
]
51+
"presets": [["react-app", { "flow": false, "typescript": true }]]
4952
}
5053
```

packages/babel-preset-react-app/create.js

+6
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ module.exports = function(api, opts, env) {
3030
var isEnvTest = env === 'test';
3131

3232
var isFlowEnabled = validateBoolOption('flow', opts.flow, true);
33+
var isTypeScriptEnabled = validateBoolOption(
34+
'typescript',
35+
opts.typescript,
36+
true
37+
);
3338
var areHelpersEnabled = validateBoolOption('helpers', opts.helpers, true);
3439
var useAbsoluteRuntime = validateBoolOption(
3540
'absoluteRuntime',
@@ -97,6 +102,7 @@ module.exports = function(api, opts, env) {
97102
useBuiltIns: true,
98103
},
99104
],
105+
isTypeScriptEnabled && [require('@babel/preset-typescript').default],
100106
].filter(Boolean),
101107
plugins: [
102108
// Strip flow types before any other transform, emulating the behavior

packages/babel-preset-react-app/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@babel/plugin-transform-runtime": "7.1.0",
3030
"@babel/preset-env": "7.1.0",
3131
"@babel/preset-react": "7.0.0",
32+
"@babel/preset-typescript": "7.1.0",
3233
"@babel/runtime": "7.0.0",
3334
"babel-loader": "8.0.4",
3435
"babel-plugin-dynamic-import-node": "2.2.0",

packages/react-scripts/config/paths.js

+38-6
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,46 @@ function getServedPath(appPackageJson) {
4646
return ensureSlash(servedUrl, true);
4747
}
4848

49+
const moduleFileExtensions = [
50+
'web.mjs',
51+
'mjs',
52+
'web.js',
53+
'js',
54+
'web.ts',
55+
'ts',
56+
'web.tsx',
57+
'tsx',
58+
'json',
59+
'web.jsx',
60+
'jsx',
61+
];
62+
63+
// Resolve file paths in the same order as webpack
64+
const resolveModule = (resolveFn, filePath) => {
65+
const extension = moduleFileExtensions.find(extension =>
66+
fs.existsSync(resolveFn(`${filePath}.${extension}`))
67+
);
68+
69+
if (extension) {
70+
return resolveFn(`${filePath}.${extension}`);
71+
}
72+
73+
return resolveFn(`${filePath}.js`);
74+
};
75+
4976
// config after eject: we're in ./config/
5077
module.exports = {
5178
dotenv: resolveApp('.env'),
5279
appPath: resolveApp('.'),
5380
appBuild: resolveApp('build'),
5481
appPublic: resolveApp('public'),
5582
appHtml: resolveApp('public/index.html'),
56-
appIndexJs: resolveApp('src/index.js'),
83+
appIndexJs: resolveModule(resolveApp, 'src/index'),
5784
appPackageJson: resolveApp('package.json'),
5885
appSrc: resolveApp('src'),
86+
appTsConfig: resolveApp('tsconfig.json'),
5987
yarnLockFile: resolveApp('yarn.lock'),
60-
testsSetup: resolveApp('src/setupTests.js'),
88+
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
6189
proxySetup: resolveApp('src/setupProxy.js'),
6290
appNodeModules: resolveApp('node_modules'),
6391
publicUrl: getPublicUrl(resolveApp('package.json')),
@@ -74,11 +102,12 @@ module.exports = {
74102
appBuild: resolveApp('build'),
75103
appPublic: resolveApp('public'),
76104
appHtml: resolveApp('public/index.html'),
77-
appIndexJs: resolveApp('src/index.js'),
105+
appIndexJs: resolveModule(resolveApp, 'src/index'),
78106
appPackageJson: resolveApp('package.json'),
79107
appSrc: resolveApp('src'),
108+
appTsConfig: resolveApp('tsconfig.json'),
80109
yarnLockFile: resolveApp('yarn.lock'),
81-
testsSetup: resolveApp('src/setupTests.js'),
110+
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
82111
proxySetup: resolveApp('src/setupProxy.js'),
83112
appNodeModules: resolveApp('node_modules'),
84113
publicUrl: getPublicUrl(resolveApp('package.json')),
@@ -105,11 +134,12 @@ if (
105134
appBuild: resolveOwn('../../build'),
106135
appPublic: resolveOwn('template/public'),
107136
appHtml: resolveOwn('template/public/index.html'),
108-
appIndexJs: resolveOwn('template/src/index.js'),
137+
appIndexJs: resolveModule(resolveOwn, 'template/src/index'),
109138
appPackageJson: resolveOwn('package.json'),
110139
appSrc: resolveOwn('template/src'),
140+
appTsConfig: resolveOwn('template/tsconfig.json'),
111141
yarnLockFile: resolveOwn('template/yarn.lock'),
112-
testsSetup: resolveOwn('template/src/setupTests.js'),
142+
testsSetup: resolveModule(resolveOwn, 'template/src/setupTests'),
113143
proxySetup: resolveOwn('template/src/setupProxy.js'),
114144
appNodeModules: resolveOwn('node_modules'),
115145
publicUrl: getPublicUrl(resolveOwn('package.json')),
@@ -120,3 +150,5 @@ if (
120150
};
121151
}
122152
// @remove-on-eject-end
153+
154+
module.exports.moduleFileExtensions = moduleFileExtensions;

packages/react-scripts/config/webpack.config.dev.js

+29-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
// @remove-on-eject-end
99
'use strict';
1010

11+
const fs = require('fs');
1112
const path = require('path');
13+
const resolve = require('resolve');
1214
const webpack = require('webpack');
1315
const PnpWebpackPlugin = require('pnp-webpack-plugin');
1416
const HtmlWebpackPlugin = require('html-webpack-plugin');
@@ -146,7 +148,7 @@ module.exports = {
146148
// https://github.com/facebook/create-react-app/issues/290
147149
// `web` extension prefixes have been added for better support
148150
// for React Native Web.
149-
extensions: ['.mjs', '.web.js', '.js', '.json', '.web.jsx', '.jsx'],
151+
extensions: paths.moduleFileExtensions.map(ext => `.${ext}`),
150152
alias: {
151153
// Support React Native Web
152154
// https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
@@ -220,7 +222,7 @@ module.exports = {
220222
// Process application JS with Babel.
221223
// The preset includes JSX, Flow, and some ESnext features.
222224
{
223-
test: /\.(js|mjs|jsx)$/,
225+
test: /\.(js|mjs|jsx|ts|tsx)$/,
224226
include: paths.appSrc,
225227
loader: require.resolve('babel-loader'),
226228
options: {
@@ -353,7 +355,7 @@ module.exports = {
353355
// its runtime that would otherwise be processed through "file" loader.
354356
// Also exclude `html` and `json` extensions so they get processed
355357
// by webpacks internal loaders.
356-
exclude: [/\.(js|mjs|jsx)$/, /\.html$/, /\.json$/],
358+
exclude: [/\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
357359
loader: require.resolve('file-loader'),
358360
options: {
359361
name: 'static/media/[name].[hash:8].[ext]',
@@ -406,7 +408,30 @@ module.exports = {
406408
fileName: 'asset-manifest.json',
407409
publicPath: publicPath,
408410
}),
409-
],
411+
// TypeScript type checking
412+
fs.existsSync(paths.appTsConfig) &&
413+
(() => {
414+
let ForkTsCheckerWebpackPlugin;
415+
try {
416+
ForkTsCheckerWebpackPlugin = require(resolve.sync(
417+
'fork-ts-checker-webpack-plugin',
418+
{ basedir: paths.appNodeModules }
419+
));
420+
} catch (e) {
421+
// Fail silently.
422+
// Type checking using this plugin is optional.
423+
// The user may decide to install `fork-ts-checker-webpack-plugin` or use `tsc -w`.
424+
return null;
425+
}
426+
427+
return new ForkTsCheckerWebpackPlugin({
428+
async: false,
429+
checkSyntacticErrors: true,
430+
tsconfig: paths.appTsConfig,
431+
watch: paths.appSrc,
432+
});
433+
})(),
434+
].filter(Boolean),
410435

411436
// Some libraries import Node modules but don't use them in the browser.
412437
// Tell Webpack to provide empty mocks for them so importing them works.

0 commit comments

Comments
 (0)