TypeScript
Summary
Bare minimum required for sharing a React component written in TypeScript.
Nobody wants to write React without JSX. As we're going to need a build step anyway and there's no sane reason to build something without TypeScript nowadays, we're going to go directly to JSX + TypeScript.
Starting from the bare example, we
- move the
index.js
tosrc/index.tsx
(to better separate code and build artefacts later), and - add
typescript
and React's types to ourdevDependencies
(see TODO for an overview over the different dependency types):pnpm add -D typescript @types/react
.
Once we set up a build step, the built library will be exposed in dist/index.js
together with a declaration file at dist/index.d.ts
, so we update the package.json
accordingly:
Setting up TypeScript
In order to compile TypeScript + JSX to vanilla JavaScript, we will add a build
script to our package.json
that runs the TypeScript compiler (we will use fancier tooling like esbuild
in the future):
By default, the TypeScript compiler doesn't really know what to do with your stuff, so you need to create a tsconfig.json
:
include
(Reference): This tells TypeScript which files to look at.compilerOptions.target
(Reference): This tells TypeScript which version of ECMAScript (aka which JavaScript standard) to compile to. When developing apps, this should be set to something sensible likeES6
. For libraries like here, we want the consuming application to have full control over its own bundling and polyfilling, so we use the most up-to-date standard, which is exposed asESNext
.compilerOptions.moduleResolution
(Reference): There's three choices here,classic
,node
andnodenext
(akanode16
). You probably never want to useclassic
in modern projects.node
references Node's CommonJS resolution algorithm. Since we want to emit an ES Module, we will usenodenext
, Node's ES Modules resolution algorithm.compilerOptions.outDir
(Reference): This tells TypeScript where to put the compiled files. We want them indist
(don't forget to add that directory to your.gitignore
!)compilerOptions.jsx
(Reference): This informs the TypeScript compiler that we will use JSX syntax. TypeScript can convert that either tocreateElement
calls throughreact
(which we used in Level 1) or newer_jsx
calls available since React 17 throughreact-jsx
(see this blog post). We want the modern stuff and will usereact-jsx
.
The library can now be published and consumed analogous to the library from Level 1. However, VSCode won't be happy with us, as we did not expose any declaration file (the file telling TypeScript which vanilla JS thing has which type, see reference). In order to do that, we extend our tsconfig.json
:
Once we run pnpm build
for our library once more (and publish it if necessary), VSCode understands the type of our imported button:
tsup
Compiling our 5 LOC, 1-component component library currently takes 2.4s on my machine. Once a library gets bigger, the compile time can grow significantly. Over the last years, a lot of fantastic Rust- and Go-based tooling has been developed. We're going to use tsup, which internally uses esbuild:
We'll use the following tsup.config.ts
config file:
Update the package.json
accordingly:
Running pnpm build
now takes 1.4s on my machine, and only 4ms of those are spent on actually compiling the library.
Watch mode
To avoid having to re-build the library everytime you change something, you can use the watch mode provided by tsup
. Simply add a script to your package.json
:
Running pnpm dev
will now re-build the library on file changes.