title: Project references in tsconfig and ambient declarations
body: <p>I am trying to configure my Typescript project to have the following directory structure:</p>
  
  <pre class="lang-none prettyprint-override"><code>src/
  ├── client/
  │   └── tsconfig.json
  │
  ├── server/
  │   └── tsconfig.json
  │
  ├── shared/
  │   ├── utils/
  │   │   └── type-utils.d.ts
  │   │
  │   └── tsconfig.json
  │
  └── tsconfig-base.json
  </code></pre>
  
  <ul>
  <li><p><code>client/tsconfig.json</code>, <code>server/tsconfig.json</code>, and <code>shared/tsconfig.json</code> each have <code>"extends": "../tsconfig-base.json"</code></p></li>
  <li><p><code>client/tsconfig.json</code> and <code>server/tsconfig.json</code> both have <code>"references": [{ "path": "../shared" }]</code></p></li>
  <li><p><code>shared/utils/type-utils.d.ts</code> is not a module, but instead contains ambient type declarations like <code>Id</code> and <code>CouldBeNull</code>, as well as a declaration merge for <code>Array</code> (defining the <code>flatMap</code> method).</p></li>
  </ul>
  
  <p>The idea being that <code>tsconfig-base.json</code> could include a lot of common settings, the individual <code>tsconfig.json</code>s could define settings relevant for that sub-project (e.g. React for <code>client/tsconfig.json</code>, Node for <code>server/tsconfig.json</code>), and <code>shared/</code> would be shared between <code>client/</code> and <code>server/</code>.</p>
  
  <p>The problem is that the ambient declarations and declaration mergin <code>shared/utils/type-utils.ts</code> is not seen by <code>client/</code> or <code>server/</code>, but only by <code>shared/</code>. This causes errors, for example <code>tsc -b client --verbose</code> produces</p>
  
  <pre class="lang-none prettyprint-override"><code>$ tsc -b client --verbose
  15:31:41 - Projects in this build:
      * shared/tsconfig.json
      * client/tsconfig.json
  
  15:31:41 - Project 'shared/tsconfig.json' is up to date because newest input 'shared/formatting/numbers.ts' is older than oldest output 'shared/formatting/numbers.js.map'
  
  15:31:41 - Project 'client/tsconfig.json' is out of date because output file 'client/client/index.js' does not exist
  
  15:31:41 - Building project 'C:/Users/KRyan/Web/5heet/client/tsconfig.json'...
  
  client/index.tsx(32,29): error TS7006: Parameter 'skill' implicitly has an 'any' type.
  client/index.tsx(80,37): error TS2339: Property 'flatMap' does not exist on type 'Ability[]'.
  client/index.tsx(80,45): error TS7006: Parameter 'ability' implicitly has an 'any' type.
  client/index.tsx(88,16): error TS7006: Parameter 'one' implicitly has an 'any' type.
  client/index.tsx(88,21): error TS7006: Parameter 'another' implicitly has an 'any' type.
  client/index.tsx(151,32): error TS7031: Binding element 'id' implicitly has an 'any' type.
  client/parts/editing.tsx(31,60): error TS2339: Property 'oneOperand' does not exist on type 'never'.
  client/parts/editing.tsx(31,81): error TS2339: Property 'operator' does not exist on type 'never'.
  client/parts/editing.tsx(31,108): error TS2339: Property 'anotherOperand' does not exist on type 'never'.
  client/parts/portal.tsx(55,61): error TS2304: Cannot find name 'Omit'.
  shared/ability.d.ts(2,33): error TS2304: Cannot find name 'Id'.
  shared/ability.d.ts(8,36): error TS2304: Cannot find name 'Id'.
  shared/ability.d.ts(12,37): error TS2304: Cannot find name 'Id'.
  shared/character.d.ts(3,35): error TS2304: Cannot find name 'Id'.
  shared/expression.d.ts(7,35): error TS2304: Cannot find name 'Id'.
  shared/expression.d.ts(30,38): error TS2304: Cannot find name 'ElementOf'.
  shared/expression.d.ts(33,118): error TS2304: Cannot find name 'Id'.
  shared/formatting/numbers.d.ts(1,41): error TS2304: Cannot find name 'Id'.
  shared/skill.d.ts(3,31): error TS2304: Cannot find name 'Id'.
  shared/utils/basic.d.ts(5,46): error TS2304: Cannot find name 'CouldBeNull'.
  </code></pre>
  
  <p>The implicit <code>any</code> types are caused directly by the inability to find the various names listed in the second half of the output.</p>
  
  <p>And, for completeness’s sake, the full files:</p>
  
  <p><code>shared/utils/type-utils.d.ts</code>:</p>
  
  <pre><code>type PropsOf&lt;T&gt; = T[keyof T];
  type ElementOf&lt;T&gt; = T extends (infer E)[] ? E : T;
  type Subtract&lt;T, U&gt; = Pick&lt;T, Exclude&lt;keyof T, keyof U&gt;&gt;;
  type Omit&lt;T, K extends keyof T&gt; = Pick&lt;T, Exclude&lt;keyof T, K&gt;&gt;;
  type CouldBeNull&lt;T&gt; = null extends T ? unknown : never;
  
  type DeepPartial&lt;T&gt; = {
      [P in keyof T]?: T[P] extends Array&lt;infer U&gt;
      ? Array&lt;DeepPartial&lt;U&gt;&gt;
      : T[P] extends ReadonlyArray&lt;infer U&gt;
      ? ReadonlyArray&lt;DeepPartial&lt;U&gt;&gt;
      : DeepPartial&lt;T[P]&gt;
  };
  
  declare class Tagged&lt;Tag&gt; {
      private 'is tagged as': [Tag];
      private dummy(): Tag;
  }
  type Id&lt;Tag&gt; = string &amp; Tagged&lt;Tag&gt;;
  
  interface Array&lt;T&gt; {
      /**
       * Calls a defined callback function on each element of an array. Then, flattens the result into
       * a new array.
       * This is identical to a map followed by a flatten of depth 1.
       *
       * @param callback A function that accepts up to three arguments. The flatMap method calls the
       * callback function one time for each element in the array.
       * @param thisArg An object to which the this keyword can refer in the callback function. If
       * thisArg is omitted, undefined is used as the this value.
       */
      flatMap&lt;U, This = undefined&gt;(
          callback: (this: This, value: T, index: number, array: T[]) =&gt; U | U[],
          thisArg?: This,
      ): U[];
  }
  </code></pre>
  
  <p><code>tsconfig-base.json</code>:</p>
  
  <pre><code>{
      "compileOnSave": true,
      "compilerOptions": {
          "target": "es2018",
          "baseUrl": ".",
          "module": "amd",
          "moduleResolution": "classic",
          "paths": {
              "csstype": [
                  "node_modules/csstype/index"
              ],
          },
          "composite": true,
          "declaration": true,
          "declarationMap": true,
          "noEmitOnError": true,
          "strictNullChecks": true,
          "allowJs": false,
          "allowUnusedLabels": false,
          "noImplicitAny": true,
          "noImplicitThis": true,
          "noImplicitReturns": true,
          "noUnusedParameters": true,
          "noFallthroughCasesInSwitch": true,
          "noStrictGenericChecks": true,
          "noUnusedLocals": true,
          "noErrorTruncation": true,
          "allowUnreachableCode": false,
          "forceConsistentCasingInFileNames": false,
          "preserveConstEnums": true,
          "sourceMap": true
      }
  }
  </code></pre>
  
  <p><code>client/tsconfig.json</code>:</p>
  
  <pre><code>{
      "extends": "../tsconfig-base.json",
      "include": [
          "**/*.ts"
      ],
      "outDir": "../../built/client",
      "compilerOptions": {
          "rootDir": "..",
          "jsx": "react",
          "types": [
              "react",
              "react-dom"
          ]
      },
      "references": [
          {
              "path": "../shared"
          }
      ],
      "watch": true
  }
  </code></pre>
  
  <p><code>server/tsconfig.json</code>:</p>
  
  <pre><code>{
      "extends": "../tsconfig-base.json",
      "include": [
          "**/*.ts"
      ],
      "outDir": "../../built/server",
      "compilerOptions": {
          "rootDir": "..",
          "module": "commonjs",
          "types": [
              "node",
              "mime-types"
          ]
      },
      "references": [
          {
              "path": "../shared"
          }
      ]
  }
  </code></pre>
  
  <p><code>shared/tsconfig.json</code>:</p>
  
  <pre><code>{
      "extends": "../../tsconfig-base.json",
      "include": [
          "**/*.ts"
      ],
      "outDir": "built/shared",
      "compilerOptions": {
          "module": "amd",
          "types": []
      }
  }
  </code></pre>