Search code examples
typescripttsconfig

Importing TypeScript declarations from local, private module into application


I have the following directory layout, leveraging yarn workspaces:

package.json
tsconfig.json
packages/
  a/
    package.json   // contains "name": "a" & "version": "1.0.0"
    index.d.ts
  b/
    package.json
    tsconfig.json
    src/
      index.ts

Inside packages/b/tsconfig.json some very basic rules:

{
  extends: 
  compilerOptions: {
    rootDir: src,
    outDir: build
  },
  include: [ **/*.ts ]
}

What I'm trying to accomplish is to import the definitions declared in packages/a/index.d.ts in packages/b/src/index.ts with a statement like import { Foo } from 'a';. I want to keep the index.d.ts in its own package because I also have a package c using the same definitions.

Without any modification the above will throw a "Module not found: Error: Can't resolve 'a'" error. Therefore, I tried the following:

  1. Add baseUrl: . and paths: { a: [ ../a/index.d.ts ] }, which now fails with "Module build failed: Error: /path/to/packages/a/index.d.ts is missing from the TypeScript compilation."
  2. Do yarn workspace b add a@1.0.0, which symlinked node_modules/a -> packages/a. Interestingly, this also resulted in a "Module not found: Error: Can't resolve 'a'", until I added main: index.d.ts to packages/a/package.json, after which I once again got "Module build failed: Error: /path/to/packages/a/index.d.ts is missing from the TypeScript compilation."
  3. Add ../../a/index.d.ts to include in packages/b/tsconfig.json after doing 2., which results in the same still. I assume this is because files outside of rootDir can't be referenced in includes.
  4. Replace rootDir: src with rootDirs: [ src, ../a ] in packages/b/tsconfig.json. Same result.

Does anyone have an idea on how to solve these compilation errors or avoid them entirely? The only strategy that comes to my mind would be creating a .js entry in package a which implements the interfaces of index.d.ts and exports them as a module, while index.d.ts declares definitions for that module. But I'd prefer to keep only the interfaces shared, instead of implementation details which may differ from package to package.


Solution

  • Could you avoid putting the .d.ts file in the main field of package.json and instead put it in types?

    package.json for a:

    {
      "name": "a",
      "version": "1.0.0",
      "main": "index.js",
      "types": "index.d.ts"
    }
    

    index.js in a:

    module.exports = {}
    

    During the build TypeScript should pull from the types field to pick up the interfaces. You shouldn't need to do anything special with rootDir, include, baseUrl, etc in the tsconfig.json.