116

I am using Visual Studio Code and have a fairly common project structure:

├── client/
│   ├── tsconfig.json
├── shared/
├── server/
│   ├── tsconfig.json
├── project.json

The two tsconfig files have different settings (e.g. the one under client/ targets ES5, the one under server/ targets ES6). Note that there is no tsconfig in the root directory.

The problem is that I want the shared directory to be included in both projects. I can't do this using tsconfig because the exclude option won't let me include a folder that is in a higher directory than the tsconfig.json, and using files I have to constantly keep the list of files up to date as it doesn't support globs.

Note that I can compile fine by adding the shared folder into tsc, what I want is for the Visual Studio Code IDE to recognise the shared code for intellisense etc.

Is the only option to wait for filesGlob?

Mike Jerred
  • 9,551
  • 5
  • 22
  • 42

5 Answers5

106

These days it is much easier as vscode has better support for this.

You can use this directory structure so all the code is independent:

├── frontend/
│   ├── src/
│   │   ├── <frontend code>
│   ├── package.json
│   ├── tsconfig.json
├── shared/
│   ├── package.json
├── backend/
│   ├── src/
│   │   ├── <backend code>
│   ├── package.json
│   ├── tsconfig.json

Then in both the backend and frontend tsconfig.json:

{
  "compilerOptions": {
    "paths": {
      "~shared/*": ["../shared/*"]
    },
    "rootDirs": [
      "./src",
      "../shared"
    ]
  }
}

To allow access to the shared code e.g.:

import { Foo } from '~shared/foo';

Old Answer

Use a single tsconfig.json for the root. And then extend it for each project (backend tsconfig.server.json, frontend tsconfig.webpack.json).

  • Root tsconfig.json include: ['src'] to ensure all files get typechecked in the IDE
  • Backend tsconfig.server.json exclude: ['src/app'] the frontend files
  • Frontend : tsconfig.webpack.json exclude: ['src/server'] the backend files

Folder Structure

├── src/
│   ├── app/    < Frontend
│   ├── server/ < Backend
│   ├── common/ < Shared
├── tsconfig.json
├── tsconfig.server.json
├── tsconfig.webpack.json

Config Files

tsconfig.json

{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true
  },
  "include": [
    "src"
  ]
}

tsconfig.webpack.json

{
  "extends": "./tsconfig.json",
  "exclude": [
    "src/app"
  ]
}

tsconfig.server.json

{
  "extends": "./tsconfig.json",
  "exclude": [
    "src/server"
  ]
}

More

Example lesson (by me)

General Grievance
  • 4,555
  • 31
  • 31
  • 45
basarat
  • 261,912
  • 58
  • 460
  • 511
  • 14
    This will not work if those directories have colliding types. For example Jest and Cypress both have their own type definitions for the keyword `describe`, you will get a compile error due to including both types jest and cypress in the types array in the tsconfig.json. Not only that but how would VS Codes intellisense know which type definition to use. This is not the correct answer. – basickarl Oct 04 '19 at 13:32
  • 1
    How to compile using the extended config files? tsc on the root directory seems only work for the main config. Is it necessary to compile via "tsc -p childTsConfig"? – Mikhail Dec 10 '19 at 07:29
  • I couldn't get this to work in vscode 1.43.0 with the set up described the IDE would only look at `/tsconfig.json`. I had to create `/tsconfig.root.json`, and `/server/tsconfig.json`. – Mr5o1 Mar 11 '20 at 06:54
  • 6
    Attention: if you extend a tsconfig file, properties get overwritten. So for example, you can't *add* to `include` array, you must overwrite it. – Drarig29 Apr 23 '20 at 21:33
  • @Drarig29 yes. Hence I used a base include and excluded the ones the specific project doesn't care about – basarat Apr 24 '20 at 00:02
  • 2
    Yes, your answer is fine, it was just an advice for future readers... I thought extending would add to arrays, but it's not the case – Drarig29 Apr 24 '20 at 00:12
  • does it overwrite compiler options as well? @Drarig29 – Manny Jun 29 '20 at 13:44
  • 1
    @Mansi it overwrites properties that you overwrite, of course. That's the matter of extending. It doesn't overwrite the whole `compilerOptions` object though. It merges the base config with what you extend... Just take a look at the next answer, it has an example of what you asked. – Drarig29 Jun 29 '20 at 14:02
  • @basickarl you're right but do you have a solution for this? – DavidWorldpeace Nov 30 '20 at 09:58
  • 7
    **This does not solve the problem** VScode will use `tsconfig.json` and thus not use the frontend/backend specific config when editing these files. Hence VScode will display warnings when editing frontend files, as `tsconfig.json` doesn't include the relevant sections. – mikemaccana Dec 17 '20 at 15:06
29

As others have mentioned, the existing answer does not solve the problem if the frontend and backend have different types - which is in nearly every case, as frontend code supports the DOM (and not the node.js standard library) whereas backend code supports the node.js standard library (and generally not the DOM).

Having a top level tsconfig.json would mean that dom code would show up as errors in frontend code (if dom is a lib) or that dom code would be allowed in backend code (if dom is omitted).

Here's a working solution:

Folder Structure

Our projects tend to be 'backend by default' with a specific folder for frontend code.

├── src/
│   ├── frontend/ < Frontend
│   │     ├── `tsconfig.json` (extends frontend framework defaults, eg Svelte)
│   ├── http/ < Backend
│   ├── events/ < Backend
├── tsconfig.json `tsconfig.json` (backend tsconfig)

Backend tsconfig.json

This is usually fairly minimal. We use jest for testing and the es2019 JS stdlib.

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "outDir": "dist",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "lib": ["es2019"],    
    "types": ["jest"],
  },
  "exclude": [
    "node_modules",
    "public/*",
    "src/frontend/*"
  ],
  "include": ["src/**/*"]
}

Frontend tsconfig.json

This is for Svelte but would work similarly in older frameworks. The frontend has different types because it supports .svelte files and the dom

{
  "extends": "@tsconfig/svelte/tsconfig.json",
  "compilerOptions": {
    // Default included above is es2017
    "target": "es2019",
  },
  "lib": ["es2019", "dom"],
}

Frontend specific tools

Making rollup use a separate tsconfig file:


export default {
  input: ...
  output: ...
  plugins: [
    ...
    typescript({
      tsconfig: "src/frontend/tsconfig.json",
      sourceMap: isDevelopment,
      inlineSources: isDevelopment,
    }),
    ...
  ],
   ...
};
mikemaccana
  • 110,530
  • 99
  • 389
  • 494
  • Does this handle shared code? Wouldn't the front end pull in shared code compiled with the backend tsconfig? – Joe Lapp Apr 08 '22 at 21:28
  • Rollup’s ‘input’ option would determine the entry point for front end code. Rollup would only include files imported by that entry point file (and it’s files imported by those imports). So your front end bundle would only include front end code. – mikemaccana Apr 08 '22 at 21:32
  • Right. The thing is, I have some code that needs to be shared between front end backend, such as validation code. Front end validates to provide realtime feedback, backend validates in case someone circumvents the front end. I'm in the process of setting up things as you've described to see if I can do this. – Joe Lapp Apr 08 '22 at 21:48
  • 1
    @JoeLapp makes sense, I do this all the time using this config. So yes, import what you need on front end, including shared code. Don’t import backend specific modules on the front end, just the code you want to share. – mikemaccana Apr 08 '22 at 21:53
  • So the backend tsconfig.json is at the project root? That seems strange to me; I imagine it has something to do with vscode workarounds and not the compiler, but I want to know if I'm wrong. – Daniel Kaplan May 22 '23 at 22:21
16

I answered this here: tsconfig extension answer

The gist of the answer:

you can do this by extending your base tsconfig.json file:

tsconfig extension

just do not exclude directories in the base tsconfig.json and typescript should be able to resolve your typings for you (know this is true using node_modules/@types, or the typings module)

For example:

configs/base.json:

{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}

tsconfig.json:

{
  "extends": "./configs/base",
  "files": [
    "main.ts",
    "supplemental.ts"
  ]
}

tsconfig.nostrictnull.json:

{
   "extends": "./tsconfig",
   "compilerOptions": {
     "strictNullChecks": false
   }
}
Micah Zoltu
  • 6,764
  • 5
  • 44
  • 72
weagle08
  • 1,763
  • 1
  • 18
  • 27
  • 10
    Hi, but how can I call the compiler from command prompt? I've been calling the base json file and is not working. The other files were ignored. – Everton Santos Jul 11 '17 at 21:05
  • 7
    Also how IDE recognizes custom `tsconfig.*.json`? How can I separate tsconfig between different directories? – vintprox May 06 '19 at 16:24
  • It's not up to the IDE to recognize multiple tsconfigs. You just pass the tsconfig you are wishing to have used to the typescript compiler via the --project (alias: -p) option. https://www.typescriptlang.org/docs/handbook/compiler-options.html – weagle08 May 07 '19 at 18:50
  • example code https://github.com/ozknemoy/node-ts-nodemon-starter/tree/ts-starter-app_3-tsconfigs – dimson d Oct 28 '22 at 10:57
  • This is cool and all, but if you want vscode and eslint to give proper linting of say jest.config.ts and somecomponent.ts where somecomponent.ts relies on module="bundler" and jest.config.ts relies on "importJson" true... You can't do it. Using different extensions doesn't work because vscode will load the base tsconfig.json for all the files. So you have to move your jest.config.ts to another project and do a tsconfig project reference and then run just with the different config location via cli args. – Ryan Mann Aug 21 '23 at 05:11
4

As another variant, bind npm command with next run:

{
   'start': '...',
   'buildFront': 'tsc -p tsconfig.someName.json'
}
Community
  • 1
  • 1
kosiakMD
  • 923
  • 7
  • 22
  • 2
    Why is this a -1? He is just explaining that you can select what config you would like to use specifically. Why is that wrong with the question? – Rip3rs Sep 30 '21 at 15:47
2

The new version of VSCode supports Typescript 2, add this adds support for globs in tsconfig.json with the include option. See http://www.typescriptlang.org/docs/handbook/tsconfig-json.html

Mike Jerred
  • 9,551
  • 5
  • 22
  • 42