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

TypeScript error TS2351: This expression is not constructable. #42

Closed
ajvincent opened this issue Jul 7, 2022 · 11 comments · Fixed by #45
Closed

TypeScript error TS2351: This expression is not constructable. #42

ajvincent opened this issue Jul 7, 2022 · 11 comments · Fixed by #45
Labels

Comments

@ajvincent
Copy link

ajvincent commented Jul 7, 2022

Steps to reproduce:

  1. npm install --save code-block-writer
  2. Add this file to your TypeScript project. (I named mine CodeBlockWriter.mts, to leverage TypeScript's ECMAScript module support.)
import CodeBlockWriter from "code-block-writer";

it("CodeBlockWriter can be assembled", () => {
  const writer = new CodeBlockWriter;
  expect(writer).toBeTruthy();
});
  1. Invoke tsc for this file.
_00_shared_utilities/spec/CodeBlockWriterTest.mts(4,22): error TS2351: This expression is not constructable.
  Type 'typeof import("/home/ajvincent/code-generation/cross-stitch/node_modules/code-block-writer/types/mod")' has no construct signatures.

Please advise.

TypeScript version 4.7.4
NodeJS version 16.14.0

@dsherret
Copy link
Owner

dsherret commented Jul 7, 2022

That's strange... I'm not sure why TypeScript is erroring there as the declaration file has a default export for the class. It executes fine in a regular mjs file.

@dsherret dsherret added the bug label Jul 7, 2022
@ajvincent
Copy link
Author

For all I know, this could be a compiler bug in tsc. I've bumped into one of those already. Would it be helpful for me to provide my tsconfig? What else might I offer to help? (Can you reproduce it? If not, how can I help?)

I did search for this compiler error, but it is a very obscure one.

@ajvincent
Copy link
Author

I wonder if this has to do with my tsconfig, which defaults to:

{
  "compilerOptions": {
    "lib": ["es2021"],
    "module": "es2022",
    "target": "es2022",
    "moduleResolution": "node16",
    "sourceMap": true,
    "declaration": true,
  },
}

Also, I found:

@dsherret
Copy link
Owner

I’m able to reproduce the issue as well. I think I’m just going to revert the esm module because it seems typescript is still buggy with esm. Just this file needs to be updated to only do a cjs build https://github.com/dsherret/code-block-writer/blob/main/scripts/build_npm.ts and then the repo retagged. I will try to remember to do it tomorrow (just falling asleep now)

@dsherret
Copy link
Owner

This should work in 11.0.2. Sorry about the issues

@ajvincent
Copy link
Author

I have some more information, which I found buried deep in TypeScript bugs.

microsoft/TypeScript#21621 (comment)

Basically,

import { default as CodeBlockWriter } from "code-block-writer";

should have worked in the old code.

@ajvincent
Copy link
Author

ajvincent commented Mar 25, 2023

I'd like to reopen this issue. I think it may have regressed.

import CodeBlockWriter from "code-block-writer";
void(new CodeBlockWriter); // TypeError: CodeBlockWriter is not a constructor

I found a work-around, based on earlier comments in this issue. First, I created a module, CodeBlockWriter.mts, which exports the constructor:

import CBW_, {
  type Options as CodeBlockWriterOptions
} from "code-block-writer";

const CodeBlockWriter = (CBW_ as unknown as {
  default: new (opts?: Partial<CodeBlockWriterOptions>) => CBW_
}).default;

export default CodeBlockWriter;
export type {
  CodeBlockWriterOptions
};

Second, in code wishing to create a CodeBlockWriter, I have to import both the original CodeBlockWriter to use as a type, and the de-wrapped constructor from the custom export.

import CBW_ from "code-block-writer";
import CodeBlockWriter from "../CodeBlockWriter.mjs";

// ...

export default 
abstract class Foo {
  readonly #classWriter = new CodeBlockWriter({ indentNumberOfSpaces: 2 });
  protected abstract buildMethodBody(
    methodName: string,
    signature: MethodSignature,
    writer: CBW_
  ) : void;
}
// ...

Third, for files just using the API, importing the original CodeBlockWriter as a type is fine:

import CodeBlockWriter from "code-block-writer";
import Foo from "./Foo.mjs";

class Bar extends Foo {
  protected buildMethodBody(
    methodName: string,
    signature: MethodSignature,
    writer: CodeBlockWriter,
  ): void
  {
    void(methodName);
    signature.args.forEach(arg => writer.writeLine(`void(${arg.key});`));
    writer.writeLine(`throw new Error("not yet implemented");`);
  }
}

Using TypeScript 5.0.2, with tsconfig:

{
  "compilerOptions": {
    "lib": [
      "es2022"
    ],
    "module": "es2022",
    "target": "es2022",
    "moduleResolution": "node",
    "sourceMap": true,
    "declaration": true
  },
  "extends": "@tsconfig/node18/tsconfig.json"
}

@dsherret
Copy link
Owner

I just took a look at the types (and I now know way too much about node module resolution than I care to know compared to back in July when I knew basically nothing) and yeah the types are incorrect.

@dsherret
Copy link
Owner

Actually, I initially thought the code was doing export = and the types were doing export default, but the code is doing the exports.default thing in cjs. I think it should be able to be consumed by doing CodeBlockWriter.default in the mjs, but I'm not sure why TypeScript doesn't understand it that way because the package is distributed as cjs. I'm just going to convert back to a dual esm/cjs package, but I think I have it right this time (maybe lol). I'll try to do it tomorrow though because it's late here.

@dsherret
Copy link
Owner

Ok, I ended up publishing this tonight anyway. Should be fixed in 12.0.0 as this is now republished as an esm and cjs package, but let me know if it doesn't work. This cjs/esm situation is a huge pain.

@ajvincent
Copy link
Author

Confirmed, the updated types work without my wrapper code.

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

Successfully merging a pull request may close this issue.

2 participants