I was following the React & Webpack TypeScript guide but found it to be outdated and broken.
Hence, I documented my own guide in wiring up TypeScript with React and webpack.
Project Layout
First, create a directory for your project:
mkdir react-webpack && cd react-webpack
Install the dependencies (I’m using yarn but you can use npm):
yarn add webpack@4 \
webpack-cli \
react \
react-dom \
@types/react \
@types/react-dom \
typescript \
awesome-typescript-loader@5
Note: If you’re using webpack@3 (and not webpack@4 with webpack-cli), then you’ll need to use awesome-typescript-loader@4.
The dependencies can be described as follows:
webpack/webpack-cliis the bundlerreact/react-domis the library we’re using to build our app@types/react/@types/react-domprovides the type definitionstypescriptis the compilerawesome-typescript-loaderis the webpack loader
TypeScript Configuration
Create a TypeScript configuration file:
touch tsconfig.json
And add the following:
{
"compilerOptions": {
"jsx": "react",
"lib": ["dom", "es2015"],
"module": "commonjs",
"noImplicitAny": true,
"outDir": "./dist/",
"sourceMap": true,
"target": "es5"
},
"include": ["./src/**/*"]
}
Options
Let’s go over each option:
compilerOptions: the compiler optionsjsx: react: supports JSX in.tsxfileslib: includes a list of library files in the compilation (specifyinges2015allows you to use ES6 syntax)module: specifies the module code generationnoImplicitAny: raises errors on expressions and declarations with an impliedanytype (to enable all strict type checking options, usestrictinstead)outDir: the output directorysourceMap: generates.map, which is useful for debuggingtarget: the target ECMAScript version to transpile down to (pick the version that supports your browser requirements)include: the files to be included (there’s alsoexclude)
First Component
Let’s create our first component:
mkdir -p src/App/ && touch src/App/index.tsx
And add the code:
// src/App/index.tsx
import * as React from 'react';
interface Props {
name: string;
}
interface State {}
export default class App extends React.Component<Props, State> {
render() {
return <h1>Hello {this.props.name}</h1>;
}
}
Why are we using import * as React from 'react' instead of import React from 'react'?
Because react uses CommonJS syntax (module.exports = ...) and not ES Module syntax (export default ...).
If you use import React from 'react', you’ll receive the TypeScript compiler error:
yarn tsc
error TS1192: Module '"react"' has no default export.
Note: However, if you really want to use import React from 'react', you can enable allowSyntheticDefaultImports in the config:
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
// ...
}
}
Render App
Let’s create our entry file:
touch src/index.tsx
And add the code:
// src/index.tsx
import * as React from 'react';
import { render } from 'react-dom';
import App from './App';
render(<App name="world" />, document.getElementById('app'));
Then create the HTML file:
mkdir public && touch public/index.html
And add the markup:
<!-- public/index.html -->
<div id="app"></div>
<script src="../dist/bundle.js"></script>
Configure Webpack
Let’s create our webpack configuration file:
touch webpack.config.js
And add the following:
// webpack.config.js
const { resolve } = require('path');
module.exports = {
mode: 'production',
entry: './src/index.tsx',
output: {
filename: 'bundle.js',
path: resolve(__dirname, './dist/'),
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'awesome-typescript-loader',
},
],
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.json'],
},
devtool: 'source-map',
};
On a high-level, this configuration file tells webpack to start at the entry file, resolve every require/import, and then output a bundle.
When webpack sees a file with the extension .ts or .tsx, it transpiles the file contents using the typescript loader.
Build Bundle
To build the bundle, run:
yarn webpack
Open App
To open the app in a browser, run:
open public/index.html
Conclusion
You can find the tutorial code here.