Typescript: Error when importing untyped JS modules

Created on 5 Apr 2017  路  42Comments  路  Source: microsoft/TypeScript

TypeScript Version: 2.2.1

Steps

  1. In a simple TS project in VSCode, add "adal-node": "^0.1.22", to package.json and run npm install. This is a JS library with no types.
  2. Import the library in your index.ts as below

Code

import adal from "adal-node";

Expected behavior:
Per this bug's resolution #3019 this code should compile with no errors

Actual behavior:
tsc.exe: 'Could not find a declaration file for module 'adal-node'. 'e:/proj/node_modules/adal-node/lib/adal.js' implicitly has an 'any' type.'

References:
see #3019 and all its linked bugs

Fixed

Most helpful comment

@Spongman Actually, you can just have a global.d.ts at your project root with following content:

declare module '*';

It automatically makes all js modules any while ts modules keep working as intended.

All 42 comments

/cc: @mhegazy

Because you have turned on noImplicitAny. You need to get around it by doing declare module 'adal-node' (or declare module "*", I have yet to try this myself)

oops! you're right that I have noImplicitAny compiler option set in my tsconfig.json. Totally forgot about it as its one of my default configs to writing TS.

I think I can work with the solution you posted on the other thread. Thanks for the quick feedback ! Appreciate it.

I tried the suggestions mentioned in the linked bug like so:

In index.ts i added this code:

declare module 'adal-node/*'{
    var _a: any;
    export = _a;
}

and i get this error:
tsc.exe : Invalid module in augmentation, module 'adal-node/*' cannot be found. Can someone help me how to do this correctly?

@balajikris Have you tried:

declare module 'adal-node' {
    var _a: any;
    export = any;
}

instead?

@masaeedu : Thanks for the comment. Yes, I did try that earlier. It also errored out, but a slightly different message.

tsc: 'Invalid module name in augmentation. Module 'adal-node' resolves to an untyped module at 'e:/proj/node_modules/adal-node/lib/adal.js', which cannot be augmented.'

So it's loading the module via the .js file also it seems. Do you have 'allowJs' set in your project config?

@billti : Thank you for the hint. I tried after setting allowJs: true and the error message remains the same as above.

declare module 'adal-node/*'{
    var _a: any;
    export = any;
}

did you mean

declare module 'adal-node/*'{
    var _a: any;
    export = _a;
}

?

@aluanhaddad -- yes ofcourse. sorry, that was a typo! updated my post

Is there any way to do something simple like import * as BabiliPlugin from "babili-webpack-plugin"; with noImplicitAny enabled without getting warning TS7016 ("could not find a declaration file...implicitly has an 'any' type")?

Is there any way to do something simple like import * as BabiliPlugin from "babili-webpack-plugin"; with noImplicitAny enabled without getting warning TS7016 ("could not find a declaration file...implicitly has an 'any' type")?

Add a declaration for your module, e.g.:

declare module "babili-webpack-plugin" {
     export ...
}

or simply:

declare module "babili-webpack-plugin";

The issue as described by the OP seems fixed.

The other issue described in https://github.com/Microsoft/TypeScript/issues/15031#issuecomment-292011200, is because a module declaration (i.e declare module 'adal-node' {... }) inside another module (i.e. a file with at least one top-level import or export) is considered an "augmentation".
What you want is to move this declaration to a global .d.ts (as you want it to be in the global scope) file instead of keeping it in your module.

@mhegazy : I'm still not sure how to correctly use this declare module and import syntax to import untyped JS modules. Could you please provide a small snippet demonstrating how to accomplish that? Thanks.

c:\test\15031>npm install adal-node
npm WARN deprecated [email protected]: Use uuid module instead
[email protected] c:\test\15031
`-- [email protected]

c:\test\15031>echo import adal from "adal-node"; > a.ts

c:\test\15031>type a.ts
import adal from "adal-node";

c:\test\15031>tsc a.ts

Interesting, so i do not have to do a declare xxx and a simple import adal from "adal-node"; as in my original bug report should work.

I'm on tsc v2.2.2 and I'm seeing error TS1192: Module ''adal-node'' has no default export. for the above usage. What version of tsc.exe is this supposed to work on?

@balajikris I've just tried it using Typescript v2.2.2 and it works fine for me. Are you sure you're not picking up a stray tsconfig.json? Are you testing in the same directory?

Thanks for checking @TAGC . I do have a .tsconfig but that's intentional and not a stray one. I'll play with this a little more to understand what i'm missing.

@balajikris probably import * as adal from "adal-node";?

Still not being able to work with adal-node in typescript
I tried creating adal-node.d.ts:

declare module 'adal-node/*'{
    var _a: any;
    export = _a;
}

my import ts file still says:

error TS7016: Could not find a declaration file for module 'adal-node'. 'node_modules/adal-node/lib/adal.js' implicitly has an 'any' type.
  Try `npm install @types/adal-node` if it exists or add a new declaration (.d.ts) file containing `declare module 'adal-node';`

@oreporan i am unable to reproduce any errors for this scenario.

One note, if your import looks like import * as adal from "adal-node/argument"; then your module declaration looks correct, if it is however import * as adal from "adal-node"; then the module declaration does not match that. consider instead declaring it as declare module 'adal-node' { .. }

Thanks
I ended up forking and making a pull request with typings and adding an index.js outside the lib, which solves the issues

why does the noImplicitAny check fire when importing .js files? surely when you're importing a .js file you're _explicitly_ declaring everything within it as any ?

it seems too heavy a burden to require everything to have a @types or for the user to create a .d.ts file just for this check not to fire.

this IMHO really kills Anders' claim of 'drop-in replacement' for javascript.

it seems too heavy a burden to require everything to have a @types

you do not need to include @types. just add:

declare module "foo";

This should be behind another config flag or another frictionless solution, using noImplicitAny is best practice for TS, but creating a d.ts file and filling it with empty module declarations is just annoying.

This is still a JS world and we're just trying to exist in it. I never had a manually created d.ts file (only auto-generated) and now it seems like a new requirement.

It is still impossible to import untyped Javascript modules.

I've spent the past few hours trying to figure out how to use adapter.js with Typescript, and I've run into no end of problems. At this point, I'm ready to give up again on Typescript.

I've saved adapter.js in the src/ directory, along with main.ts. Things I've tried that haven't worked:

import * as webrtc from 'adapter.js';

7:25 Cannot find module 'adapter.js'.

import * as webrtc from 'adapter';

7:25 Cannot find module 'adapter'.

declare module 'adapter.js';

5:16 Invalid module name in augmentation, module 'adapter.js' cannot be found.

declare module 'adapter.js';

5:16 Invalid module name in augmentation, module 'adapter' cannot be found.

import * as webrtc from './adapter.js';

7:25 Could not find a declaration file for module './adapter.js'. 'PROJECT/src/adapter.js' implicitly has an 'any' type.

declare module './adapter.js';
import * as webrtc from './adapter.js';

ERROR in PROJECT/src/main.ts
5:16 Invalid module name in augmentation. Module './adapter.js' resolves to an untyped module at ERROR in PROJECT/src/main.ts
6:25 Could not find a declaration file for module './adapter.js'. 'PROJECT/src/adapter.js' implicitly has an 'any' type.

// adapter.d.ts
declare module "adapter" {
    var _temp: any;
    export = _temp;  
}

// main.ts
declare module './adapter.js';
import * as webrtc from './adapter.js';

5:16 File 'PROJECT/src/adapter.d.ts' is not a module.
6:25 File 'PROJECT/src/adapter.d.ts' is not a module.

import adapter = require("./adapter.js");

5:1 Import assignment cannot be used when targeting ECMAScript modules. Consider using 'import * as ns from "mod"', 'import {a} from "mod"', 'import d from "mod"', or another module format instead.

Hello! Could you please clarify where do I put this declaration file:

declare module 'adal-node';

And how do I instruct compiler to actually use it?

@slavafomin it worked for me in a similar case in another project, when I've put a new index.d.ts in the project root directory (i.e. in the same directory as the tsconfig.json is located).

You can put it anywhere as long as tsc can locate it. I.e. using files or include in tsconfig.json.

I put them under a custom-typings folder.

@Spongman Actually, you can just have a global.d.ts at your project root with following content:

declare module '*';

It automatically makes all js modules any while ts modules keep working as intended.

I can confirm @SCLeoX's suggestion. I just created a root-level global.d.ts file for untyped 3rd party libs. IMO, * is a bit too heavy-handed and I'd rather be explicit, so I simply took the module that the compiler was complaining about and declared it explicitly. But, that's just my opinion.

@SCLeoX OMG THANK YOU. Hours of pounding my head against outdated docs and absolutely terrible error messages from tsc. If this simple workaround was the front page of http://www.typescriptlang.org then typescript adoption would double and it would also be the most useful thing on that website.

I've been hunting around for this answer for the better part of a month now. I found a solution that works in VS Code that isn't a wholesale disabling of all validation for javascript/typescript and also did not require that I add files/declarations to a repository that is not mine.

Add one or both of these lines to your user settings:

"typescript.suggestionActions.enabled": false,
"javascript.suggestionActions.enabled": false,

Unlike "javascript.validate.enable": false (which you should not use), the above setting will remove those annoying [ts] Could not find a declaration file for module errors for untyped module imports and it will still play nice with linters and give you appropriate and relevant errors.

In my case, the index.d.ts or global.d.ts got deleted every time I compile. Turn off noImplicitAny is the only solution works for me so far.

I took @SCLeoX's suggestion and made the pattern-matching a bit narrower.

- declare module '*' {
+ declare module '*.js' {
   const value: any
   export default value
 }

Is it possible to re-export an existing typed module and use its types for the untyped module?

I am importing a library react-final-form-html5-validation that is just a tiny wrapper around typed react-final-form.

Are there any options for this?

I have tried many many many things ......., but this looks most promising, however, react-final-form does not declare a namespace and so I cannot use its types like I can use the React below.

project/index.d.ts

/// <reference types="react-final-form" />
//// <reference types="./node_modules/react-final-form/dist/index.d.ts" />
//// <reference path="./node_modules/react-final-form/dist/index.d.ts" />
//// <amd-dependency path="./node_modules/react-final-form/dist/index.d.ts" />
// importing resolves the types, but also turns it into a module, which in turn breaks it
// import * as RFF from react-final-form; 
// import { FieldProps } from react-final-form;
// export * from react-final-form;

declare module 'react-final-form-html5-validation' {
  var Field: React.ComponentType<FieldProps>
}

project/src/index.tsx

import { Field, Abc } from 'react-final-form-html5-validation'
// correct: Module '"react-final-form-html5-validation"' has no exported member 'Abc'

// later in App.render() { return ( .....
      <Field />

// Field is of type React.ComponentType<any>

Typescript should act smarter in these cases. WHY ON EARTH we need to define a type definition if we explicitly import a JS module.

i found this to be a helpful write up. https://medium.com/@chris_72272/migrating-to-typescript-write-a-declaration-file-for-a-third-party-npm-module-b1f75808ed2

in my case i just had to place the migration file in src/@types/@okta/library-name/index.d.ts and as long as the part after @types matched the import, it was picked up. inside can be just 1 line declaring the module

Two year problem still plagues developers...

Below is an example using Angular and Laravel Mix (specific npm scripts) to help anyone else who faces this issue with an untyped package:

  1. npm install --save-dev motion-ui
  2. echo "declare module '*';" > resources/ts/global.d.ts
  3. To use in a component:
    import * as MotionUI from '../../../node_modules/motion-ui/dist/motion-ui';
  4. npm run dev

The code should transpile correctly. Also to ignore TypeScript errors one can use // @ts-ignore just above the line that errors but I would recommend resolving the error correctly.

tried all the solutions not working! :(

Same here.

image

I tried noImplicitAny, allowJs. Nothing.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

wmaurer picture wmaurer  路  3Comments

blendsdk picture blendsdk  路  3Comments

zhuravlikjb picture zhuravlikjb  路  3Comments

siddjain picture siddjain  路  3Comments

DanielRosenwasser picture DanielRosenwasser  路  3Comments