TypeScript: `"moduleResolution": "node12"` of Typescript 4.5 does not work as expected

I just tried the typescript@next and moduleResolution: 'node12' with @angular/compiler@v13, but tsc -b failed to build:

src/index.ts:1:15 - error TS2305: Module '"@angular/compiler"' has no exported member 'ParsedTemplate'.

1 import type { ParsedTemplate } from '@angular/compiler'
                ~~~~~~~~~~~~~~

src/index.ts:1:37 - error TS1471: Module '@angular/compiler' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.

1 import type { ParsedTemplate } from '@angular/compiler'
                                      ~~~~~~~~~~~~~~~~~~~

src/index.ts:2:30 - error TS7016: Could not find a declaration file for module 'synckit'. '/Users/JounQin/Workspaces/Local/test/node_modules/synckit/lib/index.cjs' implicitly has an 'any' type.
  Try `npm i --save-dev @types/synckit` if it exists or add a new declaration (.d.ts) file containing `declare module 'synckit';`

2 import { createSyncFn } from 'synckit'
                               ~~~~~~~~~

src/worker.ts:1:15 - error TS2305: Module '"@angular/compiler"' has no exported member 'ParsedTemplate'.

1 import type { ParsedTemplate } from '@angular/compiler'
                ~~~~~~~~~~~~~~

src/worker.ts:1:37 - error TS1471: Module '@angular/compiler' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.

1 import type { ParsedTemplate } from '@angular/compiler'
                                      ~~~~~~~~~~~~~~~~~~~

src/worker.ts:2:29 - error TS7016: Could not find a declaration file for module 'synckit'. '/Users/JounQin/Workspaces/Local/test/node_modules/synckit/lib/index.cjs' implicitly has an 'any' type.
  Try `npm i --save-dev @types/synckit` if it exists or add a new declaration (.d.ts) file containing `declare module 'synckit';`

2 import { runAsWorker } from 'synckit'
                              ~~~~~~~~~

src/worker.ts:5:11 - error TS2339: Property 'parseTemplate' does not exist on type 'typeof import("/Users/JounQin/Workspaces/Local/test/node_modules/@angular/compiler/index")'.

5   const { parseTemplate } = await import('@angular/compiler')
            ~~~~~~~~~~~~~


Found 7 errors.

@angular/compiler@v13 is ESM only, ParsedTemplate is typing exported from its types entry.

See https://unpkg.com/browse/@angular/compiler@13.0.0-rc.0/package.json

synckit is both commonjs and ESM compatible.

See https://unpkg.com/browse/synckit@0.6.0/package.json

I have no idea how can it be fixed on my side.


Test source codes:

// worker.ts
import type { ParsedTemplate } from '@angular/compiler'
import { runAsWorker } from 'synckit'

runAsWorker<ParsedTemplate>(async (code: string, filePath: string) => {
  const { parseTemplate } = await import('@angular/compiler')
  return parseTemplate(code, filePath, {
    preserveWhitespaces: true,
    preserveLineEndings: true,
    collectCommentNodes: true,
  })
})

_Originally posted by @JounQin in https://github.com/microsoft/TypeScript/issues/45884#issuecomment-945563236_

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 19 (11 by maintainers)

Commits related to this issue

Most upvoted comments

I tried to use worker.cts instead of worker.ts with "type": "module" today, the errors from synckit reduced, but there is still:

src/worker.cts:1:29 - error TS1471: Module 'synckit' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.

1 import { runAsWorker } from 'synckit'

I’ve tried the following in node_modules/synckit/package.json

{
  "exports": {
    "import": "./lib/index.js",
    "require": "./lib/index.cjs",
    "types": "./lib/index.d.ts"
  }
}

I still don’t understand how to fix this part.

See https://github.com/JounQin/test/tree/ts_esm for reproduction

cc @weswigham @DanielRosenwasser

synckit has a lib/index.d.ts which matches up to lib/index.js - its esm entrypoint. However, you’re importing from a cjs mode file, so the exports map isn’t resolving to that, it’s resolving to lib/index.cjs instead, and there’s no lib/index.d.cts to describe it, hence the error. synckit can either provide a lib/index.d.cts with its’ cjs entrypoint shape, or it can provide a types condition that overrides the types we look up.

@angular/compiler is esm only - your errors tell me you’re importing it in a cjs mode file. So that’s an issue. Second, @angular/compiler is borked. What do I mean by that? Their declaration files are esm mode (as indicated by package.json, but their internal imports don’t include any extensions! Their index.d.ts is just export * from "./compiler" - "./compiler" doesn’t resolve under esm resolution rules, hence why there’s no parseTemplate member. Whoever went and added the types condition to their export map didn’t actually check that their types were esm resolution compatible.

Nope! The format of the file is important information to the type system - it tells us how the module is loaded and, importantly, if there’s a default that’s the shape of the module itself when imported in esm, and if it’s an error to load it at all in cjs. At runtime a file can’t be both an esm and cjs module, and thus neither can the types for a module. (Though, as you’ve observed, nothing stops you from pulling almost the whole definition of one of the formats from the other one, in the same way you can re-export the actual implementation!)