Skip to content

typescript.SymbolTable does not properly implement the ES6 Map interface #36699

Open
@Zemnmez

Description

@Zemnmez

TypeScript Version: 3.7.3

The typescript.SymbolTable is like an ES6 Map -- typescript.d.ts describes it as 'based on the ES6 Map interface'.

However, the underlying interface typescript.UnderscoreEscapedMap does not implement the Map interface, or the Iterable, or Iterator protocols. This means it's not possible to iterate over properly without shim code, which varies from manually implementing the Iterator protocol (annoying!) to overriding the type with as Map<ts.__String, ts.Symbol> (bad!).

Code Example
import * as ts from "typescript";

const p = ts.createProgram(["t.d.ts"], {});
const c = p.getTypeChecker();
p.getSourceFiles().map(f => {
    f.forEachChild(n => {
        if (!ts.isModuleDeclaration(n)) return;
        const s = c.getSymbolAtLocation(n.name);
        if (!s) return;

        const scanMembers = (s: ts.Symbol) => {
            if (s.valueDeclaration === undefined)
                console.log("missing valueDeclaration on", s.getName());

            type m = Iterator<any>
            if (!s.exports) return;
            if (!(Symbol.iterator in s.exports))
                return console.log("not iterable", s.getName())
            for (let [/*name*/, member] of (s.exports as Map<ts.__String, ts.Symbol>)) {
                scanMembers(member);
            }
        }

        scanMembers(s);


    })
})

typescript.UnderscoreEscapedMap is not recognized as a Map due to clear(), delete(), set(), [Symbol.toStringTag] and [Symbol.iterator] being undefined.

Testing map assertion
// Type 'ReadonlyUnderscoreEscapedMap<any>' is missing the following properties
// from type 'Map<any, any>': clear, delete, set, [Symbol.iterator],
// [Symbol.toStringTag] ts(2739)
const x: Map<any, any> = void 0 as any as ts.ReadonlyUnderscoreEscapedMap<any>

Perhaps this is a compiler internals issue, as using as Map<ts.__String, ts.Symbol> makes SymbolTables work without issue. If it's not, the fix should be as simple as replacing typescript.UnderscoreEscapedMap with Map and typescript.ReadonlyUnderscoreEscapedMap with ReadonlyMap.

Search Terms:

Code

import * as ts from "typescript";

const p = ts.createProgram(["t.d.ts"], {});
const c = p.getTypeChecker();
p.getSourceFiles().map(f => {
    f.forEachChild(n => {
        if (!ts.isModuleDeclaration(n)) return;
        const s = c.getSymbolAtLocation(n.name);
        if (!s) return;

        const scanMembers = (s: ts.Symbol) => {
            if (s.valueDeclaration === undefined)
                console.log("missing valueDeclaration on", s.getName());

            type m = Iterator<any>
            if (!s.exports) return;
            if (!(Symbol.iterator in s.exports))
                return console.log("not iterable", s.getName())

            // error!!
            for (let [/*name*/, member] of s.exports) {
                scanMembers(member);
            }
        }

        scanMembers(s);


    })
})

Expected behavior:
It should be possible to iterate over a typescript.SymbolTable.

Actual behavior:
typescript.SymbolTable is considered not iterable.

Metadata

Metadata

Assignees

No one assigned

    Labels

    APIRelates to the public API for TypeScriptAwaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions