Home Blog Page 30

Monorepos with TypeScript: Nx, Turborepo Basics

0
typscript course
typscript course

Table of Contents

  • What is a Monorepo?
  • Benefits of Monorepos
  • Overview of Nx and Turborepo
    • What is Nx?
    • What is Turborepo?
  • Setting Up a Monorepo with Nx
  • Setting Up a Monorepo with Turborepo
  • Comparison of Nx vs. Turborepo
  • Best Practices for Managing a TypeScript Monorepo
  • Conclusion

What is a Monorepo?

A monorepo (short for “monolithic repository”) is a version-controlled code repository that holds multiple projects or applications in the same repository. These projects can share common dependencies, configuration files, and libraries. Unlike traditional approaches where each project has its own repository, monorepos centralize code and make it easier to manage multiple related projects.

Benefits of Monorepos

  • Centralized Management: All projects are maintained in a single codebase, which makes dependency management and version control more streamlined.
  • Shared Code: Common libraries, utilities, and configuration files can be shared across projects without duplication.
  • Consistent CI/CD: Monorepos help you standardize and unify the build and test processes across projects, reducing overhead in maintaining multiple CI/CD pipelines.
  • Atomic Changes: Monorepos allow you to make atomic changes that span multiple projects, ensuring consistency when upgrading dependencies or applying refactors.

Overview of Nx and Turborepo

Both Nx and Turborepo are modern tools designed to help developers manage monorepos, providing powerful features such as dependency graph visualization, optimized builds, and caching mechanisms. Let’s dive into what these tools are and how they can help with TypeScript-based monorepo setups.

What is Nx?

Nx is an open-source set of extensible dev tools built for managing monorepos. It was developed by the team at Nrwl and provides a set of tools to streamline the development process for large applications and libraries within a monorepo.

Nx offers a rich ecosystem with capabilities such as:

  • Powerful CLI: A command-line interface for generating, building, testing, and managing projects in a monorepo.
  • Project Graph: A visualization of the dependencies between your projects, which helps optimize builds and test strategies.
  • Computation Caching: Nx caches build outputs and tests to avoid redundant computations, speeding up build times.
  • Integration with Popular Frameworks: Nx has integrations for React, Angular, Node.js, NestJS, and more, making it easy to set up and manage projects in your monorepo.

What is Turborepo?

Turborepo is another open-source tool designed to optimize the management of monorepos, focusing on speed and developer experience. It was created by Vercel and is known for its emphasis on build caching and parallel execution to significantly reduce build times.

Key features of Turborepo include:

  • Parallel Task Execution: Turborepo allows tasks like building and testing to run in parallel, leveraging multiple CPU cores.
  • Incremental Builds: By caching build outputs and reusing them when possible, Turborepo accelerates rebuilds and deployments.
  • Task Pipelines: Turborepo helps organize tasks in pipelines, enabling fine-grained control over the order and dependencies of tasks in your monorepo.

Setting Up a Monorepo with Nx

Nx is a feature-rich tool that integrates various types of projects into a monorepo structure. Here’s how you can set up an Nx-based monorepo with TypeScript.

Step 1: Install Nx

To set up Nx, you’ll need to install the Nx CLI globally.

npm install -g nx

Step 2: Create an Nx Workspace

You can create an Nx workspace using the following command. You will be prompted to choose the type of workspace (e.g., React, Node, etc.).

npx create-nx-workspace@latest my-monorepo

This will generate a new workspace with a structure where you can start adding projects like apps or libraries.

Step 3: Add TypeScript Projects to the Workspace

To add a TypeScript project (e.g., a Node.js app or a React app), use the nx generate command.

nx generate @nrwl/node:application my-app

This will create a new TypeScript-based application in the apps/ directory.

You can also create libraries that can be shared between apps:

nx generate @nrwl/node:library my-lib

This will generate a shared library in the libs/ directory.

Step 4: Manage Dependencies and Build Configurations

Nx allows you to manage dependencies and build configurations between apps and libraries. For example, if my-app depends on my-lib, you can visualize this dependency in the project graph, and Nx will optimize the build process.

To visualize the project graph:

nx dep-graph

Setting Up a Monorepo with Turborepo

Turborepo focuses on fast builds, parallel task execution, and caching. Here’s how you can set up a Turborepo-based monorepo for TypeScript.

Step 1: Install Turborepo

First, install Turborepo globally via npm:

npm install -g turbo

Alternatively, you can use npx to run it without installing globally.

Step 2: Create a New Project

You can create a new Turborepo project by following the official documentation, which provides a starter template for TypeScript.

npx create-turbo@latest my-monorepo

This will generate a new Turborepo workspace with sample apps and libraries.

Step 3: Set Up TypeScript Projects

Inside the monorepo, you can set up multiple TypeScript-based applications and libraries. For example, you can create a new app in the apps/ directory and a shared library in the libs/ directory.

You can manage build pipelines and tasks for each of your projects in the turbo.json file.

{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
},
"test": {
"dependsOn": ["build"]
}
}
}

This file specifies how tasks are dependent on each other and how caching is handled.

Step 4: Running Tasks in Parallel

Turborepo automatically runs tasks in parallel, so tasks like building and testing can be executed faster. For example:

turbo run build

This will build all projects in the monorepo, utilizing caching and parallel execution to speed up the process.


Comparison of Nx vs. Turborepo

FeatureNxTurborepo
Primary FocusScalable monorepos with a lot of features and integrationsSpeed and parallelization of builds
CachingBuilt-in computation caching for builds, tests, and lintingOptimized build caching and parallel execution
Task PipelinesYes, with fine-grained controlYes, with simple configuration
CLI IntegrationRich CLI for generating projects, apps, libraries, etc.CLI for task execution with a focus on speed
Project GraphProvides a visual dependency graph of your projectsSimple task dependency management
IntegrationsAngular, React, Node, NestJS, etc.Focused on JavaScript/TypeScript stacks
Monorepo ConfigurationMore opinionated, includes Nx-specific configurationSimple setup with turbo.json configuration
Use CaseLarge enterprise-level applications with a lot of integrationsSmaller, faster workflows for TypeScript-heavy projects

Best Practices for Managing a TypeScript Monorepo

  1. Modularize your Code: Split your codebase into smaller, reusable libraries (e.g., utilities, components) to promote reuse across different applications within the monorepo.
  2. Use Workspaces: If your project dependencies need to be shared between multiple apps, configure TypeScript workspaces for smooth dependency management.
  3. Leverage Caching: Both Nx and Turborepo offer caching mechanisms to improve build performance. Take full advantage of these to speed up your development cycle.
  4. Set Up Continuous Integration: Ensure that your CI/CD pipelines are optimized for monorepo workflows. Use tools that can understand the dependencies between your apps and libraries.
  5. Type Checking Across Projects: Use a shared tsconfig.json and paths to ensure consistent TypeScript settings across all applications and libraries.

Conclusion

Monorepos with TypeScript can be a highly efficient way to manage large-scale projects. Nx and Turborepo are powerful tools that help streamline workflows, optimize builds, and enable parallel task execution. Whether you prioritize feature richness (Nx) or blazing-fast builds (Turborepo), both tools offer excellent solutions for TypeScript-based monorepo management. By following best practices and leveraging these tools, you can create a scalable and efficient development environment for your teams.

Advanced tsconfig.json: Compiler Flags and Strictness for Large Projects

0
typscript course
typscript course

Table of Contents

  • Why Advanced TypeScript Configuration is Essential for Large Projects
  • Key Compiler Options in tsconfig.json
    • Strictness Flags
    • Type Checking Flags
    • Module Resolution Flags
    • Performance-Optimizing Flags
    • Output and Source Mapping Flags
  • Organizing a Large Codebase with tsconfig.json
  • Example tsconfig.json for Large Projects
  • Conclusion

Why Advanced TypeScript Configuration is Essential for Large Projects

In large TypeScript projects, an advanced tsconfig.json setup allows you to:

  • Enforce stricter typing to catch issues early in development.
  • Optimize compilation performance when working with hundreds or thousands of files.
  • Ensure maintainability as the project grows, making the codebase easier to navigate and less error-prone.
  • Leverage modern JavaScript features (like ESNext features) while maintaining backward compatibility with older environments or libraries.

By fine-tuning the compiler flags and leveraging the tsconfig.json options, you can tailor the TypeScript behavior to suit the scale and complexity of your project.


Key Compiler Options in tsconfig.json

Here’s an in-depth breakdown of the key compiler options and flags you can use for large projects:

1. Strictness Flags

Strict flags help enforce the highest level of type safety and avoid potential issues.

  • strict: Enables all strict type-checking options at once. This is the most important flag to use in large projects. "strict": true
  • noImplicitAny: Disallows variables, parameters, and return types from being implicitly any. "noImplicitAny": true
  • strictNullChecks: Ensures that null and undefined are treated as distinct types and not assignable to other types. "strictNullChecks": true
  • strictFunctionTypes: Ensures function types are checked more rigorously, preventing more subtle bugs. "strictFunctionTypes": true
  • strictPropertyInitialization: Ensures that class properties are initialized in the constructor before use. "strictPropertyInitialization": true
  • noImplicitThis: Ensures that this in functions is not implicitly typed as any. "noImplicitThis": true
  • alwaysStrict: Makes the entire project use strict mode ("use strict") automatically in every JavaScript file. "alwaysStrict": true
  • esModuleInterop: Ensures compatibility between CommonJS and ES Modules, which is especially helpful in large codebases using external libraries. "esModuleInterop": true

2. Type Checking Flags

These options provide fine-grained control over the type-checking process:

  • noUnusedLocals: Ensures that variables declared but not used trigger an error. This is crucial for maintaining a clean codebase. "noUnusedLocals": true
  • noUnusedParameters: Similar to noUnusedLocals, but for function parameters. "noUnusedParameters": true
  • noImplicitReturns: Ensures that functions with a non-void return type always return a value. "noImplicitReturns": true
  • noFallthroughCasesInSwitch: Prevents fallthrough in switch cases, where one case accidentally runs into another. "noFallthroughCasesInSwitch": true

3. Module Resolution Flags

When dealing with large projects, module resolution and how TypeScript finds files becomes critical.

  • moduleResolution: The algorithm TypeScript uses to locate files. Options include node and classic. Use node for modern workflows. "moduleResolution": "node"
  • baseUrl: The base directory for resolving non-relative module imports. It’s helpful for avoiding long relative import paths. "baseUrl": "./src"
  • paths: Allows custom paths to be mapped, which is especially helpful for monorepos or large codebases with complex module structures. "paths": { "@components/*": ["src/components/*"], "@utils/*": ["src/utils/*"] }

4. Performance-Optimizing Flags

For large projects, build performance can become a concern. Use these flags to optimize the compilation process.

  • incremental: Enables incremental compilation to speed up the build by caching information between builds. "incremental": true
  • skipLibCheck: Skips type checking of declaration files (.d.ts). This speeds up the compilation process for large projects but sacrifices some safety. "skipLibCheck": true
  • isolatedModules: Ensures each file is treated as an isolated module (similar to how Babel compiles). This is required for transpiling with tools like Babel. "isolatedModules": true
  • maxNodeModuleJsDepth: Limits the number of directories TypeScript looks into when resolving JavaScript files in node_modules. Useful for avoiding long-resolution times in large node module trees. "maxNodeModuleJsDepth": 2

5. Output and Source Mapping Flags

For debugging and source mapping, these flags help with managing the output of your TypeScript project.

  • sourceMap: Generates corresponding .map files for debugging. "sourceMap": true
  • outDir: Specifies the directory for the compiled JavaScript files. "outDir": "./dist"
  • declaration: Generates .d.ts declaration files for TypeScript consumers. "declaration": true
  • declarationMap: Creates sourcemaps for .d.ts files, which is useful for debugging type definitions. "declarationMap": true
  • removeComments: Removes comments from the output JavaScript files. Useful for production builds to reduce file size. "removeComments": true

Organizing a Large Codebase with tsconfig.json

In large projects, you may have multiple configurations for different parts of the project (e.g., frontend, backend, tests, etc.). You can use extends and references to manage these configurations.

  • extends: Allows you to share a common base tsconfig.json among different parts of the project. { "extends": "./tsconfig.base.json" }
  • references: Useful for monorepos. You can set up projects to reference each other, enabling faster builds and dependency resolution. { "compilerOptions": { "composite": true }, "references": [ { "path": "../other-project" } ] }

Example tsconfig.json for Large Projects

Here is an example tsconfig.json for a large project:

{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"declaration": true,
"declarationMap": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitReturns": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"moduleResolution": "node",
"esModuleInterop": true,
"baseUrl": "./src",
"paths": {
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
},
"incremental": true,
"skipLibCheck": true,
"isolatedModules": true,
"sourceMap": true,
"removeComments": true,
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

This setup balances strict type checking with performance optimizations and modularity for a large project.


Conclusion

Advanced tsconfig.json configurations are essential for managing large TypeScript projects. By leveraging strict type checking, performance optimization flags, and modular configuration strategies, you can ensure your codebase remains maintainable, safe, and scalable as it grows.

This configuration approach ensures that you catch errors early, enforce best practices, and maximize build efficiency, making it a crucial part of a successful TypeScript project strategy.

Building and Publishing TypeScript Libraries to npm

0
typscript course
typscript course

Table of Contents

  • Why Publish a TypeScript Library?
  • Key Considerations Before Publishing
  • Setting Up Your Library Project
  • Configuring tsconfig.json for Libraries
  • Writing TypeScript Code
  • Bundling Your Library
  • Preparing package.json
  • Managing .npmignore or files field
  • Building the Library
  • Publishing to npm
  • Versioning and Publishing Best Practices
  • Conclusion

Why Publish a TypeScript Library?

Publishing your own TypeScript library allows you to:

  • Share reusable code (e.g., UI components, utilities, SDKs).
  • Establish credibility as an open-source contributor or expert.
  • Empower your team internally or support the larger developer community.

A properly published TypeScript library ensures:

  • Consumers get typed support out of the box.
  • Both JavaScript and TypeScript projects can easily consume it.

Key Considerations Before Publishing

  • Type Definitions: Ensure your .d.ts files are available.
  • Build Output: Ship clean, compiled JavaScript.
  • Bundling: Use tools like tsup, rollup, esbuild if necessary.
  • ESModules vs CommonJS: Decide the module target.
  • Tree Shaking: Export ES modules for better optimization.
  • License and Readme: Add a LICENSE and README.md.

Setting Up Your Library Project

Create a new folder for your library:

mkdir my-awesome-lib
cd my-awesome-lib
npm init -y

Install development dependencies:

npm install --save-dev typescript

Optionally, you may add bundlers like tsup, rollup, or vite later if needed.

Project structure:

my-awesome-lib/
├── src/
│ └── index.ts
├── package.json
├── tsconfig.json
├── README.md
└── LICENSE

Configuring tsconfig.json for Libraries

Here’s a minimal tsconfig.json for building a library:

{
"compilerOptions": {
"target": "ES2019",
"module": "ESNext",
"declaration": true,
"declarationMap": true,
"outDir": "dist",
"rootDir": "src",
"moduleResolution": "Node",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}

Key options:

  • declaration: generates .d.ts files.
  • outDir: where to put the build output.
  • strict: ensures strong typing.

Writing TypeScript Code

Inside src/index.ts:

export function greet(name: string): string {
return `Hello, ${name}!`;
}

You can structure your library better if needed (e.g., create folders like utils, components, etc.).


Bundling Your Library (Optional but Recommended)

Instead of just using tsc, you can bundle using tsup, rollup, or esbuild.

Example with tsup:

Install tsup:

npm install --save-dev tsup

Add a build script in your package.json:

{
"scripts": {
"build": "tsup src/index.ts --format esm,cjs --dts"
}
}

This command will:

  • Create ESM and CommonJS outputs.
  • Generate .d.ts types automatically.

Tsup is fast and perfect for most libraries.


Preparing package.json

Update your package.json carefully:

{
"name": "my-awesome-lib",
"version": "1.0.0",
"description": "An awesome TypeScript library",
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "tsc"
},
"repository": {
"type": "git",
"url": "https://github.com/yourusername/my-awesome-lib.git"
},
"keywords": [
"typescript",
"library"
],
"author": "Your Name",
"license": "MIT"
}

Important Fields:

  • main: CommonJS entry.
  • module: ESModule entry (for modern bundlers).
  • types: Points to the TypeScript declarations.
  • files: Only publish necessary files (like dist/).

Managing .npmignore or files Field

Instead of .npmignore, it’s better to use the files field in package.json.

If you still prefer .npmignore, make sure you exclude:

  • /src
  • /node_modules
  • /tests
  • /example
  • /scripts

Example .npmignore:

src/
node_modules/
tests/
example/
scripts/
tsconfig.json

Building the Library

Run the build:

npm run build

It will generate:

  • JavaScript files (.js or .mjs)
  • Type definition files (.d.ts) inside the dist/ folder.

Publishing to npm

  1. Login to npm:
npm login
  1. Publish:
npm publish --access public
If your package name is scoped (e.g., @your-org/my-lib), you must use --access public to make it public.
  1. Done! Your library is live!

Versioning and Publishing Best Practices

  • Follow Semantic Versioning (semver):
    • MAJOR.MINOR.PATCH
    • Major = breaking changes
    • Minor = new features (no breaking)
    • Patch = fixes
  • Add proper CHANGELOG.md.
  • Use GitHub Releases or npm Release Notes.
  • Tag your Git commits (git tag v1.0.0).
  • Set up CI/CD to auto-publish on pushes (e.g., GitHub Actions).

Conclusion

Publishing a TypeScript library to npm the right way ensures:

  • Developers get a clean and typed experience.
  • Your code is reliable and future-proof.
  • You and your brand build credibility in the community.

Quick Summary Workflow:

Code ➔ Build ➔ Publish ➔ Repeat (with proper versioning)

Setting Up Pre-Commit Hooks: Husky + lint-staged for TypeScript Monorepos

0
typscript course
typscript course

Table of Contents

  • What are Pre-Commit Hooks?
  • Why Use Husky and lint-staged?
  • Installing Husky and lint-staged
  • Basic Setup for Husky
  • Configuring lint-staged
  • Full Example: TypeScript + ESLint + Prettier Workflow
  • Best Practices
  • Conclusion

What are Pre-Commit Hooks?

Pre-commit hooks are scripts that automatically run before you commit your code to Git.

You can use them to:

  • Lint code
  • Format code
  • Run tests
  • Prevent bad commits

This saves you (and your team) from introducing broken or badly formatted code into the repository.


Why Use Husky and lint-staged?

  • Husky: Makes it super easy to manage Git hooks like pre-commit, pre-push, etc.
  • lint-staged: Runs linters and formatters only on changed (staged) files, making it fast.

Together, they ensure only clean, well-formatted code gets committed — without slowing down developers.


Installing Husky and lint-staged

At the root of your monorepo:

# Install both as dev dependencies
npm install --save-dev husky lint-staged

or if you’re using yarn:

yarn add -D husky lint-staged

Basic Setup for Husky

  1. Enable Husky in your repo:
npx husky install
  1. Make sure it’s installed automatically after npm install:

In your package.json:

{
"scripts": {
"prepare": "husky install"
}
}
The prepare script makes sure Husky hooks are available after someone installs dependencies.
  1. Add a Pre-commit Hook:
npx husky add .husky/pre-commit "npx lint-staged"

This creates a .husky/pre-commit file that will run lint-staged every time you commit.


Configuring lint-staged

Now configure lint-staged in your package.json:

{
"lint-staged": {
"**/*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"**/*.{js,json,css,md,yml}": [
"prettier --write"
]
}
}

Explanation:

  • For .ts and .tsx files:
    • First, run ESLint and auto-fix issues.
    • Then, format with Prettier.
  • For other file types (.js, .json, .css, .md, .yml):
    • Only format using Prettier.

Full Example: TypeScript + ESLint + Prettier Workflow

Your package.json would look like:

{
"scripts": {
"prepare": "husky install",
"lint": "eslint . --ext .ts,.tsx",
"format": "prettier --write ."
},
"devDependencies": {
"husky": "^9.0.0",
"lint-staged": "^15.0.0",
"eslint": "^8.0.0",
"prettier": "^3.0.0",
"typescript": "^5.0.0"
},
"lint-staged": {
"**/*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"**/*.{js,json,css,md,yml}": [
"prettier --write"
]
}
}

And your .husky/pre-commit file would contain:

#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged

Best Practices

Keep pre-commit hooks lightweight.
(Only lint and format staged files, not full project.)

Fail fast.
If lint or formatting fails, block the commit.
Fix and re-stage before committing again.

Integrate with CI/CD.
Pre-commit is local safety.
You should still run full lints/tests in CI pipelines to catch missed issues.

Keep Husky configuration in Git
(.husky directory should be committed.)


Conclusion

Setting up Husky + lint-staged creates an automatic quality gate for your TypeScript monorepo.

Without even thinking about it, your team will:

  • Catch ESLint errors early
  • Maintain formatting standards
  • Save time during code review
  • Ship cleaner code

Summary Workflow:

Developer → git add → git commit → (Husky triggers) → lint-staged runs → commit succeeds or fails based on code quality ✅

Setting Up EditorConfig for TypeScript Monorepos

0
typscript course
typscript course

Table of Contents

  • What is EditorConfig?
  • Why Use EditorConfig in a Monorepo?
  • Installing EditorConfig
  • Writing the .editorconfig File
  • EditorConfig vs Prettier/ESLint: How They Work Together
  • Best Practices for Monorepos
  • Conclusion

What is EditorConfig?

EditorConfig is a simple file format (.editorconfig)
and a set of plugins that help maintain consistent coding styles
across different editors and IDEs.

It ensures all developers use the same:

  • Indentation style (tabs vs spaces)
  • Line endings (LF vs CRLF)
  • Final newlines
  • Charset (UTF-8, etc.)

It does not replace ESLint or Prettier — it complements them.


Why Use EditorConfig in a Monorepo?

In a monorepo, you often have multiple teams, tools, and environments.

Problems without EditorConfig:

  • One package uses spaces, another tabs.
  • Some files have Windows CRLF endings, others have Unix LF.
  • Inconsistent trailing newlines.

Using one .editorconfig at the root ensures basic code consistency across the entire repository — even before ESLint or Prettier run.


Installing EditorConfig

Most modern editors natively support EditorConfig:

  • VS Code: Built-in support (no extension needed).
  • JetBrains IDEs (WebStorm, IntelliJ): Built-in.
  • Atom, Sublime Text: Needs free plugin.

You can also add a command-line linter if needed:

npm install --save-dev editorconfig-checker

Writing the .editorconfig File

Place this at your monorepo root:
(monorepo-root/.editorconfig)

# EditorConfig is awesome: https://EditorConfig.org

root = true

# Default settings for all files
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

# Special rules for Markdown files
[*.md]
trim_trailing_whitespace = false

# Special rules for JSON files
[*.json]
indent_size = 2

# YAML files
[*.yml]
indent_size = 2

# Shell scripts
[*.sh]
end_of_line = lf
insert_final_newline = true

Explanation:

  • root = true: This tells EditorConfig to stop looking for files above this one.
  • [*]: Apply to all files by default.
  • indent_style = space: Use spaces instead of tabs.
  • indent_size = 2: 2-space indentation (common in TypeScript).
  • end_of_line = lf: Always use Unix-style LF (important for cross-platform devs).
  • insert_final_newline = true: Always add a blank line at the end of each file.
  • trim_trailing_whitespace = true: Remove trailing spaces on save (except markdown).
  • Markdown ignores trimming, because trailing spaces can be significant in markdown formatting.

EditorConfig vs Prettier/ESLint: How They Work Together

FeatureEditorConfigPrettierESLint
Basic indentation, EOL⚡ (via plugin)
Code formatting (full)
TypeScript-specific rules
  • EditorConfig handles basic editor behavior.
  • Prettier handles full code formatting (quotes, commas, spacing, etc).
  • ESLint handles code correctness and best practices.

Tip: Prettier can respect .editorconfig automatically if editorconfig: true is set in .prettierrc.

Example .prettierrc:

{
"editorconfig": true,
"semi": true,
"singleQuote": true,
"printWidth": 80
}

Best Practices for Monorepos

✅ Always place .editorconfig at the root of the repo.
✅ Keep .editorconfig minimal and generic.
✅ Let Prettier handle specific formatting rules beyond tabs/spaces/newlines.
✅ Configure Prettier to respect .editorconfig.
✅ Educate team members to enable EditorConfig plugins/extensions if needed.


Conclusion

Setting up EditorConfig in a monorepo is essential for basic formatting consistency across multiple packages, apps, libraries, and teams.

It acts as a lightweight safety net before Prettier or ESLint even touch your files.

Combined with Prettier and ESLint, it makes your TypeScript monorepo:

  • Cleaner
  • More standardized
  • Less prone to frustrating “space vs tab” debates