TypeScript example
In this guide, you'll learn how to integrate TypeScript into moon. We'll be using project references, as it ensures that only affected projects are built, and not the entire repository.
Begin by installing typescript
and any pre-configured tsconfig packages in your root. We suggest
using the same version across the entire repository.
- Yarn
- Yarn (classic)
- npm
- pnpm
- Bun
yarn add --dev typescript tsconfig-moon
yarn add --dev typescript tsconfig-moon
# If using workspaces
yarn add --dev -W typescript tsconfig-moon
npm install --save-dev typescript tsconfig-moon
pnpm add --save-dev typescript tsconfig-moon
# If using workspaces
pnpm add --save-dev -w typescript tsconfig-moon
bun install --dev typescript tsconfig-moon
Setup
Since typechecking is a universal workflow, add a typecheck
task to
.moon/tasks/node.yml
with the following parameters.
tasks:
typecheck:
command:
- 'tsc'
# Use incremental builds with project references
- '--build'
# Always use pretty output
- '--pretty'
# Use verbose logging to see affected projects
- '--verbose'
inputs:
# Source and test files
- 'src/**/*'
- 'tests/**/*'
# Type declarations
- 'types/**/*'
# Project configs
- 'tsconfig.json'
- 'tsconfig.*.json'
# Root configs (extended from only)
- '/tsconfig.options.json'
outputs:
# Matches `compilerOptions.outDir`
- 'lib'
Projects can extend this task and provide additional parameters if need be, for example.
tasks:
typecheck:
args:
# Force build every time
- '--force'
Configuration
Root-level
Multiple root-level TypeScript configs are required, as we need to define compiler options that are shared across the repository, and we need to house a list of all project references.
To start, let's create a tsconfig.options.json
that will contain our compiler options. In our
example, we'll extend tsconfig-moon for convenience.
Specifically, the tsconfig.workspaces.json
config, which enables ECMAScript modules, composite
mode, declaration emitting, and incremental builds.
{
"extends": "tsconfig-moon/tsconfig.projects.json",
"compilerOptions": {
// Your custom options
"moduleResolution": "nodenext",
"target": "es2022"
}
}
We'll also need the standard tsconfig.json
to house our project references. This is used by
editors and tooling for deep integrations.
{
"extends": "./tsconfig.options.json",
"files": [],
// All project references in the repo
"references": []
}
The
typescript.rootConfigFileName
setting can be used to change the root-level config name and thetypescript.syncProjectReferences
setting will automatically keep project references in sync!
Project-level
Every project will require a tsconfig.json
, as TypeScript itself requires it. The following
tsconfig.json
will typecheck the entire project, including source and test files.
{
// Extend the root compiler options
"extends": "../../tsconfig.options.json",
"compilerOptions": {
// Declarations are written here
"outDir": "lib"
},
// Include files in the project
"include": ["src/**/*", "tests/**/*"],
// Depends on other projects
"references": []
}
The
typescript.projectConfigFileName
setting can be used to change the project-level config name.
Sharing
To share configuration across projects, you have 3 options:
- Define settings in a root-level config. This only applies to the parent repository.
- Create and publish an
tsconfig base
npm package. This can be used in any repository. - A combination of 1 and 2.
For options 2 and 3, if you're utilizing package workspaces, create a local package with the following content.
{
"compilerOptions": {
// ...
"lib": ["esnext"]
}
}
Within another tsconfig.json
, you can extend this package to inherit the settings.
{
"extends": "tsconfig-company/tsconfig.json"
}
FAQ
How to preserve pretty output?
TypeScript supports a pretty format where it includes codeframes and color highlighting for
failures. However, when tsc
is piped or the terminal is not a TTY, the pretty format is lost. To
preserve and always display the pretty format, be sure to pass the --pretty
argument!