As promised in my previous blog React Custom Toolchain Setup I have added typescript to the setup, though with slight twist.
I started from same base again by cloning https://github.com/paradoxinversion/creating-a-react-app-from-scratch
Upgrade to latest
Then upgraded the dependencies to latest by
yarn upgrade --latest
This left me with one issue with webpack
development server start script. Updated it to suite version 5
"scripts": {
"start": "webpack serve",
}
Add typescript
At this point we are already using babel
as transpiler with webpack
.
There were multiple approaches to introduce the typescript
- Add a different loader like
ts-loader
orawesome-typescript-loader
and run entire transcompilation through it, removebabel
- Chain multiple loaders like
ts-loader
andbabel-loader
manually - Use
babel-loader
with@babel/preset-typescript
which allows for transpiling the TypeScript code into JavaScript and then shipping it to babel's pipeline.
I have taken approach third approach for simple reasons
- Babel is super configurable . Checkout https://github.com/babel/awesome-babel A list of awesome Babel plugins, presets, etc. to explore possibilities with Babel
- It's easier to manage one compiler
- It's faster to compile as type-checking doesn't happen at the time of transpilation
- Since type checking is separate step, you could do it when you are ready. Helps in refactors or when you are trying things out.
So lets dive in changes
Add @babel/preset-typescript
and related dependencies
yarn add -D @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators @babel/plugin-proposal-object-rest-spread
Add typescript
yarn add -D typescript fork-ts-checker-webpack-plugin
Update .babelrc
as follows
{
"presets": [
["@babel/env", { "targets": { "browsers": "last 2 versions" } }],
"@babel/preset-typescript",
"@babel/preset-react"
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }],
"@babel/plugin-proposal-object-rest-spread",
"react-hot-loader/babel"
]
}
TypeScript has a couple of extra features which Babel needs to know about (via @babel/plugin-proposal-decorator, @babel/plugin-proposal-class-properties
plugins listed above).
Add tsconfig.json
file to configure TypeScript type checking
{
"compilerOptions": {
// Target latest version of ECMAScript.
"target": "esnext",
// Search under node_modules for non-relative imports.
"moduleResolution": "node",
// Process & infer types from .js files.
"allowJs": true,
// Don't emit; allow Babel to transform files.
"noEmit": true,
// Enable strictest settings like strictNullChecks & noImplicitAny.
"strict": true,
// Disallow features that require cross-file information for emit.
"isolatedModules": true,
// Import non-ES modules as default imports.
"esModuleInterop": true,
// For transpiling JSX
"jsx": "react",
},
"include": [
"src"
]
}
Add a script to run type checking into package.json
"scripts": {
"start": "webpack serve",
"check-types": "tsc"
},
This will help you check types by running yarn run check-types
(watch mode: yarn run check-types -- --watch
) and ensure TypeScript is happy with your code.
Lets configure webpack via following webpack.config.js
const path = require("path");
const webpack = require("webpack");
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
module.exports = {
entry: "./src/index.tsx",
mode: "development",
module: {
rules: [
{
test: /\.(j|t)sx?$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: {
cacheDirectory: true,
}
},
{
test: /\.css$/,
use: ["style-loader", "css-loader"]
},
{
test: /\.(png|jpg|gif)$/i,
dependency: { not: ['url'] },
type: 'asset/resource'
},
]
},
resolve: { extensions: ["*", ".js", ".jsx",".ts", ".tsx"] },
output: {
path: path.resolve(__dirname, "dist/"),
publicPath: "/dist/",
filename: "bundle.js"
},
devtool:"cheap-module-source-map",
devServer: {
contentBase: path.join(__dirname, "public/"),
port: 3000,
publicPath: "http://localhost:3000/dist/",
hotOnly: true
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new ForkTsCheckerWebpackPlugin()
]
};
Allow me to explain
It's a Webpack plugin that runs TypeScript type checker on a separate process.
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// and
...
plugins: [
...
new ForkTsCheckerWebpackPlugin()
]
...
Following lines make sure that all js, tsx, jsx files are transpiled by babel-loader
with the help of presets and plugins mentioned in .babelrc
...
module: {
rules: [
{
test: /\.(j|t)sx?$/,
exclude: /(node_modules|bower_components)/,
loader: "babel-loader",
options: {
cacheDirectory: true,
}
},
...
]
}
...
resolve: { extensions: ["*", ".js", ".jsx",".ts", ".tsx"] },
...
Webpack 5 provides Asset Modules for the static assets like icons, images etc. without configuring additional loaders.
...
module: {
rules: [
...
{
test: /\.(png|jpg|gif)$/i,
dependency: { not: ['url'] },
type: 'asset/resource'
},
...
]
...
}
...
Configuring source maps for debugging purpose can be customized as per need from here
...
devtool:"cheap-module-source-map",
...
Lets continue with setup
Add global.d.ts
file to declare any global types e.g.
declare module '*.png' {
const content: string
export default content
}
This allows for loading the png
files with the help of import as shown below. Above shown webpack.config.js
allows us to get the path for same.
...
import image from './React-and-typescript.png';
...
const App: FC = () => {
return (
<div className="App">
<h1> Hello, World! with typescript </h1>
<img src={image} />
</div>
);
}
Last thing would be to add types. Add types by installing them
yarn add -D @babel/preset-typescript @types/react @types/react-dom @types/react-hot-loader
Last thing remaining is rename all your files which have JSX to .tsx.
For all above changes you can refer the commit https://github.com/bmhaskar/creating-a-react-app-from-scratch/pull/3/commits/db5e17fadf9b6b22e31991a0e83dc76db92fab19
All set and done!
You should be able to run your development environment with hot module replacement by
yarn start
Summary
You went through the Babel
and Webpack
based tooling for TypeScript
code base. Current setup allows you to do development and provides robust, extensible development environment.
There are few other things like Build Generation and Testing which I will be covering in next blog stay tune.