Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

This expression is not callable. error on typescript helmet 5.0.1 #344

Closed
noam-honig opened this issue Jan 4, 2022 · 15 comments · Fixed by #345
Closed

This expression is not callable. error on typescript helmet 5.0.1 #344

noam-honig opened this issue Jan 4, 2022 · 15 comments · Fixed by #345

Comments

@noam-honig
Copy link

noam-honig commented Jan 4, 2022

The full error is:

src/server/index.ts:6:9 - error TS2349: This expression is not callable.
  Type 'typeof import("C:/try/t21/helet-issue/node_modules/helmet/dist/index")' has no call signatures.

6 app.use(helmet({ contentSecurityPolicy: false }));
          ~~~~~~

Previously in version 4.6.0 it works great, but in the latest helmet 5.0.1 version it does not work.
/src/server/index.ts

import * as express from 'express';
import * as helmet from 'helmet';


const app = express();
app.use(helmet({ contentSecurityPolicy: false })); // this line shows the error.
app.listen(3002, () => console.log("Server started"));

Here's my tsconfig:

{
    "compileOnSave": false,
    "compilerOptions": {
        "outDir": "./dist/server",
        "module": "commonjs",
        "baseUrl": "./",
        "moduleResolution": "node",
        "target": "es2017",
        "lib": [
            "es2020",
            "dom"
        ]
    },
}

You can see this in code sandbox:
https://codesandbox.io/s/s4fuq

When I change the import to:

import helmet from 'helmet';

I get a runtime error:
TypeError: (0 , helmet_1.default) is not a function

@EvanHahn
Copy link
Member

EvanHahn commented Jan 4, 2022

tl;dr: change your import.

-import * as helmet from 'helmet';
+import helmet from 'helmet';

 // ...

 app.use(helmet());

I believe this is working as expected. This kind of import should never work:

// This should not work:
import * as foo from 'my-example-import';
foo();

That's because import * imports the whole package as a namespace. TypeScript sometimes lets you get away with this, but it's not technically valid JavaScript as far as I understand.

You can fix it in one of two ways:

  1. Import Helmet's default export:

    import helmet from 'helmet';
    
    // ...
    
    app.use(helmet());
  2. Import Helmet as a namespace, then use its subpackages. Be careful not to forget any! (I don't recommend this option unless you know what you're doing.)

    import * as helmet from 'helmet';
    
    // ...
    
    app.use(helmet.contentSecurityPolicy());
    app.use(helmet.dnsPrefetchControl());
    // ...

I'm going to close this issue because I think things are working as intended, but let me know if that's wrong and I can reopen.

@EvanHahn EvanHahn closed this as completed Jan 4, 2022
@noam-honig
Copy link
Author

Hi Evan, see the suffix of my report - I've tried it and I get the following error when I run node:

TypeError: (0 , helmet_1.default) is not a function

@noam-honig
Copy link
Author

See code sandbox:
https://codesandbox.io/s/s4fuq

Run npm run start:

import * as express from "express";
import helmet from "helmet";

const app = express();
app.use(helmet({ contentSecurityPolicy: false }));
app.listen(3002, () => console.log("Server started"));

Error:

TypeError: (0 , helmet_1.default) is not a function
    at Object.<anonymous> (/sandbox/src/server/index.ts:5:15)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Module._compile (/sandbox/node_modules/source-map-support/source-map-support.js:568:25)
    at Module.m._compile (/tmp/ts-node-dev-hook-03339809695552054.js:69:33)
    at Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at require.extensions.<computed> (/tmp/ts-node-dev-hook-03339809695552054.js:71:20)
    at Object.nodeDevHook [as .ts] (/sandbox/node_modules/ts-node-dev/lib/hook.js:63:13)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:12)
    at Module.require (internal/modules/cjs/loader.js:974:19)
[ERROR] 13:46:52 TypeError: (0 , helmet_1.default) is not a function

@EvanHahn
Copy link
Member

EvanHahn commented Jan 4, 2022

Hmm, not sure what's going on. I'll reopen.

Three ideas:

  1. Enable esModuleInterop in your TypeScript configuration
  2. Use TypeScript's CommonJS import (import helmet = require("helmet"))
  3. Probably a big change, but change your package to use ECMAScript Modules instead of CommonJS. This probably means updating your package.json and tsconfig.

@paulo-ventura
Copy link

Setting esModuleInterop to true on tsconfig.json worked for me:

@noam-honig
Copy link
Author

Setting esModuleInterop also worked for me, it broke the import * as express, but I fixed that as well.

It is a breaking change between 4.6 and current - it's up to you.

Thanks for the help

@EvanHahn
Copy link
Member

EvanHahn commented Jan 4, 2022

That's good!

  1. Do you feel like your problem is solved?
  2. Do you think Helmet needs to change anything about how it's exported or documented?

@noam-honig
Copy link
Author

noam-honig commented Jan 4, 2022 via email

@EvanHahn
Copy link
Member

EvanHahn commented Jan 4, 2022

Totally. I tried my best to avoid breaking changes like this, but looks like I goofed a bit.

I'm going to close this issue, but I'll think about how to improve this (now and in the next major version).

@EvanHahn EvanHahn closed this as completed Jan 4, 2022
@GuyMev
Copy link

GuyMev commented Jan 6, 2022

I'm having the same issue, enabling esModuleInterop messes my whole project and I would like to avoid that.

@EvanHahn
Copy link
Member

EvanHahn commented Jan 6, 2022

@GuyMev Does import helmet = require("helmet") work for you?

@MitchellCash
Copy link
Contributor

MitchellCash commented Jan 6, 2022

@EvanHahn I was also seeing this issue in my project and like @GuyMev I would like to avoid modifying my tsconfig.json just for this library.

But the good news, I might have a possible fix to solve everyone's problems here. I'm not the biggest ESM/CJS/TS expert, but will open a pull request for review and see what you think.

@PAStheLoD
Copy link

Hello,

import helmet = require("helmet") doesn't work, but const helmet = require("helmet") does. (But TS sees it as "any", and I haven't found a way to import just HelmetOptions.)

(With NestJS and TS 4.5.4, and ESM interop false.)

Is there any drawback to ESMinterop?

@EvanHahn
Copy link
Member

@PAStheLoD I think esModuleInterop should be safe to use.

I still need to review #345 which may fix this problem, but I've been busy.

@EvanHahn
Copy link
Member

I just released helmet@5.0.2 which should fix this problem. Please try updating to see if that fixes things for you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging a pull request may close this issue.

6 participants