TypeScript. Ambient declaration was not found when importing "module/subdirectory" npm package

Viewed 1271

I am developing a library called 'vee-type-safe' for runtime type checking. Everything worked beautifully untill I added a subdirectory /express and a file /express/index.ts where I export some ExpressJS middleware type checking factories. So I have the following structure:

vee-type-safe
|- build
|- package.json
|- declarations
|  |- is-iso-date.d.ts
|
|- tsconfig.json
|- index.ts       // lightweight core library
|- express
   |-index.ts     // express middleware factories

In express/index.ts file I import my library core '../index.ts' module. In my core module I have the following import:

import isISODate = require('is-iso-date');

'is-iso-date' package has no typings, so I created declarations directory with is-iso-date.d.ts which is as simple as this:

declare module 'is-iso-date' {
    function isISODate(suspect: string): boolean;
    export = isISODate;
}

I added "typeRoots": [ ..., "declarations"] to tsconfig.json

I added "types": "build/index.d.ts" to package.json

When I run tsc in my package everything compiles with no errors. But when I install my 'vee-type-safe' library as a dependency to some project via npm and try to compile it, I get the following error:

Could not find a declaration file for module 'is-iso-date'. 
'/home/tegeran/projects/is-iso-date-issue/node_modules/is-iso-date/index.js'
implicitly has an 'any'type.
Try `npm install @types/is-iso-date` if it exists or add a new declaration (.d.ts)
file containing `declare module 'is-iso-date';`

1 import isISODate = require('is-iso-date');

This happens only when I import 'vee-type-safe/express' submodule. When I import my core 'vee-type-safe' module, no errors are generated. What am I missing here? I created a github repo with a bare minimum project to demonstrate this error

1 Answers

When you run tsc on the outer project, the tsconfig.json file of vee-type-safe is not in effect, so nothing forces tsc to load vee-type-safe/declarations/is-iso-date.d.ts. For an import of vee-type-safe, this is OK because the types field of vee-type-safe/package.json redirects to vee-type-safe/build/index.d.ts, which does not refer to is-iso-date since vee-type-safe/index.ts uses is-iso-date only in the implementation and does not expose any types from it. However, an import of vee-type-safe/express bypasses vee-type-safe/package.json and loads vee-type-safe/express/index.ts directly, and that file imports vee-type-safe/index.ts, which imports is-iso-date, and you get the error. More importantly, the import of vee-type-safe/express won't work at runtime because it doesn't resolve to a .js file.

You have a few options to fix this, none of them great:

  1. (Deleted)
  2. Have the outer project import vee-type-safe/build/express, which will resolve to vee-type-safe/build/express/index.d.ts.
  3. Remove the outDir option from vee-type-safe so that the .d.ts files are generated alongside the .ts files.
  4. Redirect vee-type-safe/express (and each other submodule path you want other projects to be able to import) individually to the proper files under build by manually creating either a pair of .js and .d.ts files that import the real paths or a package.json file with main and types fields that refer to the real paths. (Update: It looks like main is enough because TypeScript will try changing the extension of the main path.)

See this issue for additional discussion.

Related