Skip to content

[api-extractor] Error: Symbol not found for identifier: Readonly #1304

Closed

Description

We're using api-extractor at decentraland/explorer. We've tried updating TS to version 3.4 to make use of incremental builds but we just got this not-friendly error:

api-extractor 7.1.6  - https://api-extractor.com/

Using configuration from ~/decentraland/explorer/packages/decentraland-ecs/api-extractor.json

Error: Symbol not found for identifier: Readonly

It also outputs the same message when using verbose mode.

Any thoughts? I just know that TS 3.4 also add a lot new stuff regarding to readonly behaviour

Activity

  1. octogonz commented on Jun 2, 2019

    @octogonz
    Collaborator

    Internally API Extractor uses the 3.4.x engine, so this is probably a bug.

    If you can provide a branch and some instructions for reproducing that error, I can debug it.

  2. added
    bugSomething isn't working as intended
    needs more infoWe can't proceed because we need a better repro or an answer to a question
    on Jun 2, 2019
  3. tnc1997 commented on Jun 24, 2019

    @tnc1997
    Contributor

    Hi Pete Gonzalez (@octogonz)

    I'm unsure if my issue is related to this, however I'm receiving an error as follows:

    Error: Symbol not found for identifier: Map

    I've pushed the code to a branch in my repository located here: tnc1997/ts-utils.

    You should be able to reproduce this issue by:

    • Uncommenting the apiExtractor task in the gulpfile.js files.
    • Running rush update && rush build.

    Many Thanks

  4. added
    repro confirmedThe issue comments included repro instructions, and the maintainers reproduced the problem
    and removed
    needs more infoWe can't proceed because we need a better repro or an answer to a question
    on Jun 25, 2019
  5. octogonz commented on Jun 25, 2019

    @octogonz
    Collaborator

    I'll take a look

  6. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    Hrmmm... That is really strange.

    If I invoke your API Extractor installation from the command line like this:

    cd ts-utils\libraries\map
    node ..\..\common\temp\node_modules\.registry.npmjs.org\@microsoft\api-extractor\7.2.1\node_modules\@microsoft\api-extractor\bin\api-extractor run --local
    

    ...then it works just fine. But if it gets invoked by gulp, then it fails.

  7. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    The failure is that the compiler engine doesn't know how to interpret Map<T1, T2> in your code below. In technical jargon, typeChecker.getSymbolAtLocation(identifierNode) cannot find the symbol:

    map/lib/entries.d.ts

    /**
     * Returns a two dimensional array of the entries of a map.
     * @param map - the map to get the entries of
     * @returns an array of the entries of the map
     * @public
     */
    export declare function entries<T1, T2>(map: Map<T1, T2>): Array<[T1, T2]>;"

    Ah I see why it doesn't happen from the command-line. When you build with Gulp, (by design) the rush-stack-compiler-3.2 task configures the typescriptCompilerFolder to point to this folder:

    ts-utils\common\temp\node_modules\.registry.npmjs.org\typescript\3.2.4\node_modules\typescript
    

    ...whereas API Extractor 7.2.1 when invoked from the command line would use the folder from TypeScript 3.4.5 (which is the compiler engine that AE uses for its analysis).

    (The idea behind typescriptCompilerFolder is to ask the 3.4.5 compiler engine to analyze your project using the lib folder from 3.2.4. This avoids certain compile errors that can result from language changes between compiler releases. For example, the new readonly keyword. Although the lib folder can be redirected, API Extractor cannot safely load a different actual compiler engine, because the engine API sometimes has breaking changes between compiler versions.)

    Comparing the two compiler folders, I can see that lib.es2015.collection.d.ts changed from this in TypeScript 3.2.4:

    interface MapConstructor {
        new(): Map<any, any>;
        new<K, V>(entries?: ReadonlyArray<[K, V]> | null): Map<K, V>;
        readonly prototype: Map<any, any>;
    }

    ...to this in TypeScript 3.4.5 (adding readonly):

    interface MapConstructor {
        new(): Map<any, any>;
        new<K, V>(entries?: ReadonlyArray<readonly [K, V]> | null): Map<K, V>;
        readonly prototype: Map<any, any>;
    }

    We expect the 3.4.5 compiler engine to be backwards compatible and understand these definitions that are missing readonly. I wonder why that isn't working. Perhaps the compiler internals somehow make assumptions about the Map class.

  8. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    Ron Buckton (@rbuckton) / Ryan Cavanaugh (@RyanCavanaugh) Do you have any idea why TypeScript 3.4.5 would fail to work with lib/lib.es2015.collection.d.ts from TypeScript 3.2.4?

    By "fail to work with" I mean that typeChecker.getSymbolAtLocation(identifierNode) returns undefined for a Map node.

    The typescriptCompilerFolder setting gets applied like this:
    https://github.com/microsoft/web-build-tools/blob/069a92e45016b8d879aa50d31f6d5d7f36f091e5/apps/api-extractor/src/api/CompilerState.ts#L137-L177

  9. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    Actually I get this error even if I upgrade your project to use rush-stack-compiler-3.4 which uses 3.4.5. It must be something wrong with the ts.ParsedCommandLine constructed by _updateCommandLineForTypescriptPackage().

  10. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    When typescriptCompilerFolder is omitted, the program.getSourceFiles() list is this:

    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es5.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.core.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.collection.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.generator.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.iterable.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.promise.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.proxy.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.reflect.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.symbol.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts"
    "ts-utils/libraries/map/lib/entries.d.ts"
    "ts-utils/libraries/map/lib/max.d.ts"
    "ts-utils/libraries/map/lib/min.d.ts"
    "ts-utils/libraries/map/lib/index.d.ts"
    

    ...whereas when it is specified, then we end up with this:

    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.es2015.d.ts"
    "ts-utils/common/temp/node_modules/.registry.npmjs.org/typescript/3.4.5/node_modules/typescript/lib/lib.d.ts"
    "ts-utils/libraries/map/lib/entries.d.ts"
    "ts-utils/libraries/map/lib/max.d.ts"
    "ts-utils/libraries/map/lib/min.d.ts"
    "ts-utils/libraries/map/lib/index.d.ts"
    

    This is definitely a bug with _updateCommandLineForTypescriptPackage().

  11. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    A couple problems:

    • We need to add "lib.es2015.d.ts" (and probably other similar files) to the OTHER_BUILTIN_LIBRARIES list. That will avoid incorrectly bringing in lib.d.ts
    • We need to figure out why lib.es2015.d.ts is not bringing in any additional files.

    Its implementation looks like this:
    lib/lib.es2015.d.ts

    /// <reference no-default-lib="true"/>
    /// <reference lib="es5" />
    /// <reference lib="es2015.core" />
    /// <reference lib="es2015.collection" />
    /// <reference lib="es2015.generator" />
    /// <reference lib="es2015.promise" />
    /// <reference lib="es2015.iterable" />
    /// <reference lib="es2015.proxy" />
    /// <reference lib="es2015.reflect" />
    /// <reference lib="es2015.symbol" />
    /// <reference lib="es2015.symbol.wellknown" />

    It seems like maybe the commandLine.options.noLib = true option is preventing these references from being followed. (?)

  12. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    Here we go: Looks like we got broken by TypeScript PR# 29022 ("Do not process library reference directives with noLib set").

  13. tnc1997 commented on Jun 26, 2019

    @tnc1997
    Contributor

    Thank you very much for investigating the issue, it's pointed me in the direction of a workaround!

    I can workaround the issue by changing the ts-utils\libraries\map\tsconfig.json file as follows:

    {
      "extends": "./node_modules/@microsoft/rush-stack-compiler-3.2/includes/tsconfig-base.json",
      "compilerOptions": {
        "module": "es2015",
        "target": "es5",
        "lib": [
          "es5",
          "es2015.collection",
          "es2015.iterable"
        ]
      }
    }
    

    As you can see, I've removed the reference to lib.es2015.d.ts and explicitly added the required libs.

  14. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    Thomas Clark (@tnc1997) that's good news! We should still fix this issue, however. Could I ask you to preserve your repro branch until we have solved the underlying issue?

  15. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    In microsoft/TypeScript#29022 (comment) Ron Buckton (@rbuckton) said:

    Do you need to set noLib? Including a reference for one of the older built-in libs should result in the compiler encountering a /// <reference no-default-lib="true" /> comment and preventing it from loading the default lib for your options.

    Another approach, since you are loading the compiler programmatically, is to provide a custom CompilerHost to ts.createProgram that customizes the behavior of the getDefaultLibLocation and getDefaultLibFileName methods.

    I wasn't aware of the CompilerHost.getDefaultLibLocation API. It's probably the best solution, since it will eliminate the _updateCommandLineForTypescriptPackage() logic entirely.

    It seems that wrapping CompilerHost will also help us to solve API Extractor issue #1310.

  16. added
    effort: mediumNeeds a somewhat experienced developer
    help wantedIf you're looking to contribute, this issue is a good place to start!
    and removed
    help wantedIf you're looking to contribute, this issue is a good place to start!
    on Jun 26, 2019
  17. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    Here's a PR: #1341

    This works great. I confirmed that the ts-utils repro is solved for both rush-stack-compiler-3.2 and rush-stack-compiler-3.4. The resulting code is much simpler and more robust.

    Thanks Ron Buckton (@rbuckton) for suggesting this!

    And thanks Thomas Clark (@tnc1997) for taking the time to report this and provide a repro!

  18. octogonz commented on Jun 26, 2019

    @octogonz
    Collaborator

    I merged the PR. It will be published as API Extractor 7.2.2.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working as intendedeffort: mediumNeeds a somewhat experienced developerrepro confirmedThe issue comments included repro instructions, and the maintainers reproduced the problem

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions