Go to Definition in Typescript Monorepos

This will be a quick guide for how to get "Go to Definition" working in typescript monorepos instead of the *.d.ts files created by the build step. Colin McDonnell wrote a great walkthrough of the different approaches to solving this problem, along with all of the tradeoffs. For this post, we'll just talk about how to use the recommended solution: "customConditions".

Configuring exports in package.json

First, you'll want to specify the typescript file that acts as the entrypoint for each of your exports.

"exports": {
"./package.json": "./package.json",
".": {
"my-internal": "./src/main.ts",
"types": "./dist/main.d.ts",
"default": "./dist/main.js"
},
},

In this case, I have chosen the custom condition of my-internal, but this could be almost anything (stay away from import, require, types, default, etc.).

If you have other exports, add the ts file to those too. It's important that the my-internal condition is the first one listed, otherwise it will not be used.

"exports": {
"./package.json": "./package.json",
".": {
"my-internal": "./src/main.ts",
"types": "./dist/main.d.ts",
"default": "./dist/main.js"
},
"./other": {
"my-internal": "./src/other.ts",
"types": "./dist/other.d.ts",
"default": "./dist/other.js"
}
},

Configuring the tsconfig.json

Use the custom condition in all of your monorepos tsconfig's. In this case, it is helpful if all your tsconfigs extend a single base config.

{
"compilerOptions": {
"customConditions": ["my-internal"]
}
}

In case you use vite

In your vite.config.ts:

export default {
resolve: {
conditions: ["my-internal"],
},
// this part is for vite 6 support
environments: {
ssr: {
resolve: {
conditions: ["my-internal"],
},
},
},
};

A simple example and farewell

Here is an example PR where we moved the tanstack/query monorepo from the tsconfig paths technique, over to the customCondition approach: tanstack/query#9042

Hopefully you find this helpful and hopefully more monorepos support working "go to definition"!