惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

aimingoo的专栏
aimingoo的专栏
月光博客
月光博客
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
阮一峰的网络日志
阮一峰的网络日志
博客园_首页
Last Week in AI
Last Week in AI
The Cloudflare Blog
IT之家
IT之家
Hugging Face - Blog
Hugging Face - Blog
美团技术团队
S
SegmentFault 最新的问题
量子位
大猫的无限游戏
大猫的无限游戏
Recent Announcements
Recent Announcements
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Microsoft Security Blog
Microsoft Security Blog
云风的 BLOG
云风的 BLOG
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
I
InfoQ
人人都是产品经理
人人都是产品经理
G
Google Developers Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Engineering at Meta
Engineering at Meta

Nx Blog

Sharing Tailwind CSS Styles Across Apps in a Monorepo | Nx Blog How SiriusXM Stays Competitive by Iterating and Getting to Market Fast | Nx Blog Agentic Experience Is the New Developer Experience | Nx Blog Nx Joins the Linux Foundation and the Agentic AI Foundation | Nx Blog A Monorepo Is NOT a Monolith | Nx Blog Why we deleted (most of) our MCP tools | Nx Blog Teach Your AI Agent How to Work in a Monorepo | Nx Blog How Broadcom stays efficient and nimble with monorepos | Nx Blog Why Monorepos are King in the Age of AI | Nx Blog Nx 2026 Roadmap: Expanding Agent Autonomy, Improving Performance, Better Polyglot and More | Nx Blog End to End Autonomous AI Agent Workflows with Nx | Nx Blog Autonomous Agents at Scale | Nx Blog Scaling 700+ Projects: How Nx Became a 'No-Brainer' for Caseware | Nx Blog Configure Tailwind v4 with Angular in an Nx Monorepo | Nx Blog The Missing Multiplier for AI Agent Productivity | Nx Blog A Year of Nx Webinars | Nx Blog Wrapping Up 2025 | Nx Blog Nx 22.3 Release: Angular 21 Support, tsgo Compiler, and Prettier v3 | Nx Blog Nx Cloud Release: Agent Resource Usage | Nx Blog Nx Platform Outperforms DIY Cache by 5x | Nx Blog An Nx Carol: Past, Present, and Future of Your Monorepo | Nx Blog Nx 22.1 Release: Terminal UI on Windows, Storybook 10, Vitest 4, and more! | Nx Blog The Compounding Effect: How Nx Features Multiply Performance Gains | Nx Blog 10 Monorepo Myths Debunked: Separating Fact from Fiction | Nx Blog Nx Cloud Release: Enterprise Task Analytics | Nx Blog Watch and Rebuild Storybook Dependencies with Nx | Nx Blog Book - React for Enterprise: Timeless Architecture for Enterprise Apps | Nx Blog Beyond Remote Cache: Unlock 70% More CI Performance | Nx Blog Nx 22 Release: Expanding the build platform | Nx Blog What's the Point of Generating All This Code If You Can't Merge It? | Nx Blog
Everything You Need to Know About TypeScript Project Refe...
Zack DeRose · 2025-01-28 · via Nx Blog

TypeScript Project References Series

Consider the following workspace:

.
├─ is-even
│ ├─ index.ts
│ └─ tsconfig.json
├─ is-odd
│ ├─ index.ts
│ └─ tsconfig.json
└─ tsconfig.json

And here we can see the relevant code:

export function isEven(n: number): boolean {
  return n % 2 === 0;
}
import { isEven } from 'is-even';

export function isOdd(n: number): boolean {
  return !isEven(n);
}

If we try to run our build script on the is-odd project, we see that TypeScript is unable to run the tsc command because at the TypeScript level, is-odd is not aware of the is-even module:

index.ts:1:24 - error TS2792: Cannot find module 'is-even'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?

1 import { isEven } from 'is-even';
                         ~~~~~~~~~


Found 1 error in index.ts:1

TypeScript needs to be informed as to how to find the module named is-even. The error message here actually suggests that we may have forgotten to add aliases to the paths option. To do this we can adjust our tsconfig.json file at the root of the monorepo:

{
  "compilerOptions": {
    "paths": {
      "is-even": ["./is-even/index.ts"],
      "is-odd": ["./is-odd/index.ts"]
    }
  }
}

By having the individual tsconfig.json files extend this base config, they will all get these paths, and now our build command will work.

The biggest downsides with this approach is that it does not enforce any boundaries within your monorepo. At the TypeScript level we treat the entire monorepo as one "unit". The TypeScript path aliases we defined, while seeming to create boundaries, are really just a nicer alternative for relative imports.

To solve this, TypeScript introduced Project References. Let's have a look.

TypeScript Project References

By adding boundaries at the TypeScript level, we can significantly cut down on the "surface area" that TypeScript has to contend with when doing its job. This way, rather than TypeScript seeing our entire monorepo as one unit, it can now understand our workspace as a series of connected "islands" or nodes.

Islands of TypeScript

To add this to our previous example, we'll adjust the tsconfig.json file for is-odd since it depends on is-even (note that the references field is the only difference from the is-even/tsconfig.json file):

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "references": [{ "path": "../is-even" }]
}

Note that we still actually need path aliases for our example. This is because we still need a mechanism to resolve the location in the import statement:

import { isEven } from 'is-even';

There are alternatives to path aliases to allow for this name to be resolved. The most recent enhancements in Nx use the workspaces functionality of your package manager of choice (npm/pnpm/yarn/bun) as the way of resolving these names. With a few more adjustments to this set up, we can now use the -b or --build option when building is-odd. One of these is turning on the composite compiler option for each project, which we can do by setting the compilerOption of composite to true at the root tsconfig.json file - since our other tsconfig.json files for the 2 different projects already extend our root file:

{
  "compilerOptions": {
    "paths": {
      "is-even": ["./is-even/index.ts"],
      "is-odd": ["./is-odd/index.ts"]
    },
    "composite": true
  }
}

Let's run our build now with the --verbose flag on:

Projects in this build:
    * is-even/tsconfig.json
    * is-odd/tsconfig.json

Project 'is-even/tsconfig.json' is out of date because buildinfo file 'is-even/tsconfig.tsbuildinfo' indicates that file 'is-odd/index.ts' was root file of compilation but not any more.

Building project '/Users/zackderose/monorepo-project-references/is-even/tsconfig.json'...

Project 'is-odd/tsconfig.json' is out of date because buildinfo file 'is-odd/tsconfig.tsbuildinfo' indicates that program needs to report errors.

Building project '/Users/zackderose/monorepo-project-references/is-odd/tsconfig.json'...

Notice our filesystem now:

.
├─ is-even
│  ├─ index.d.ts
│  ├─ index.js
│  ├─ index.ts
│  ├─ tsconfig.json
│  └─ tsconfig.tsbuildinfo
├─ is-odd
│  ├─ index.d.ts
│  ├─ index.js
│  ├─ index.ts
│  ├─ tsconfig.json
│  └─ tsconfig.tsbuildinfo
└─ tsconfig.json

Notice how both is-even AND is-odd now have a compiled index.d.ts declaration file and index.js. They also both have a tsconfig.tsbuildinfo file now (this holds the additional data TypeScript needs to determine which builds are needed). With the --build option, TypeScript is now operating as a build orchestrator - by finding all referenced projects, determining if they are out-of-date, and then building them in the correct order.

Why This Matters

As a practical/pragmatic developer - the TLDR of all of this information is project references allow for more performant builds.

We've put together a repo to demonstrate the performance gains, summarized by this graphic:

results of the perf measurements for TypeScript project references

In addition to the time savings we saw reduced memory usage (~< 1GB vs 3 GB). This makes sense given what we saw about how project references work. This is actually a very good thing for CI pipelines, as exceeding memory usage is a common issue we see with our clients for their TypeScript builds. Less memory usage means we can use smaller machines, which saves on the CI costs.

Can I use Project References in Nx?

Yes. You benefit from the performance gains of TypeScript project references the most in large monorepos. However, this is also when the biggest downsides of project references are felt, namely having to manually manage all the references in various tsconfig.json files. This is what we help automate in Nx.

We're going to dive deeper into the new Nx experience with project references in one of the next articles of the series, but TL;DR, you can experiment with the setup now by either using the --preset=ts:

npx create-nx-workspace@latest foo --preset=ts

Or alternatively by appending the --workspaces flag to other presets:

npx create-nx-workspace@latest reactmono --preset=react --workspaces

Note, Angular doesn't work with TypeScript project references yet but we're looking into various options to make it happen.

Next up

Stay tuned for our next article in the series about managing TypeScript packages in monorepos.