typescript, ts-morph, resolving parameter type in `export function fn_record(a: Record<string, any>): void {}`

The code being compiled and processed is:

export function fn_record(a: Record<string, any>): void {}

The node tree after compilation is shown below under File node tree.

What I want to do is use ts-morph module to produce somewhere a structure equivalent (up to arbitrary names) to that produced by :

export function fn_index(a: {[k : string]: any}): void {}

which looks like

{
  "kind": 190,
  "kindStr": "MappedType",
  "childkeys": [
    "typeParameter",
    "type"
  ],
  "typeParameter": {
    "kind": 159,
    "kindStr": "TypeParameter",
    "childkeys": [
      "name",
      "constraint"
    ],
    "name": {
      "kind": 78,
      "kindStr": "Identifier",
      "symbolref": {
        "id": -1,
        "name": "k",
        "flagStrings": [
          "TypeParameter"
        ],
        "flags": 262144
      }
    },
    "constraint": {
      "kind": 147,
      "kindStr": "StringKeyword"
    }
  },
  "type": {
    "kind": 128,
    "kindStr": "AnyKeyword"
  }
}

I read the answer to a similar question, but haven't managed to apply it correctly. (That question & answer didn't use ts-morph but I think ts-morph should have equivalent capability using ts-morph.Symbol#getTypeAtLocation.)

I have tried the following using the ts-morph module:

  const expsymbols = sourceFile.getExportSymbols();
  const decl = expsymbols[0].getDeclarations()[0]!;
  t.assert.ok(tsm.Node.isFunctionDeclaration(decl));

  decl.getParameters().forEach((p) => {
    const type1 = p
      .getNameNode()
      .getSymbol()!
      .getTypeAtLocation(p.getNameNode());
    const type1Decl = type1.getSymbol()!.getDeclarations()[0];
    t.comment(`-----------1------------`);
    t.comment(makePrintable(type1Decl.compilerNode, checker.compilerObject))

    const type2 = p.getType();
    const type2Decl = type2.getSymbol()!.getDeclarations()[0];
    t.comment(`-----------2------------`);
    t.comment(makePrintable(type2Decl.compilerNode, checker.compilerObject))

    const type3 = p
      .getNameNode()
      .getSymbol()!
      .getTypeAtLocation(p.getTypeNode()!);
    const type3Decl = type3.getSymbol()!.getDeclarations()[0];
    t.comment(`-----------3------------`);
    t.comment(makePrintable(type2Decl.compilerNode, checker.compilerObject))

All approaches 1,2,3 produce the same output as follows, and don't substitute 'string' for 'K' and 'any' for 'T'. I think that the solution is to point the right function at the right location, but further attempts have so far produced only errors. Any advice would be appreciated.

#    -----------3------------
#    {
#      "kind": 190,
#      "kindStr": "MappedType",
#      "childkeys": [
#        "typeParameter",
#        "type"
#      ],
#      "typeParameter": {
#        "kind": 159,
#        "kindStr": "TypeParameter",
#        "childkeys": [
#          "name",
#          "constraint"
#        ],
#        "name": {
#          "kind": 78,
#          "kindStr": "Identifier",
#          "symbolref": {
#            "id": -1,
#            "name": "P",
#            "flagStrings": [
#              "TypeParameter"
#            ],
#            "flags": 262144
#          }
#        },
#        "constraint": {
#          "kind": 173,
#          "kindStr": "TypeReference",
#          "childkeys": [
#            "typeName"
#          ],
#          "typeName": {
#            "kind": 78,
#            "kindStr": "Identifier",
#            "symbolref": {
#              "id": 1,
#              "name": "K",
#              "flagStrings": [
#                "TypeParameter"
#              ],
#              "flags": 262144
#            }
#          }
#        }
#      },
#      "type": {
#        "kind": 173,
#        "kindStr": "TypeReference",
#        "childkeys": [
#          "typeName"
#        ],
#        "typeName": {
#          "kind": 78,
#          "kindStr": "Identifier",
#          "symbolref": {
#            "id": 2,
#            "name": "T",
#            "flagStrings": [
#              "TypeParameter"
#            ],
#            "flags": 262144
#          }
#        }
#      }
#    }

File node tree

{
  "kind": 251,
  "kindStr": "FunctionDeclaration",
  "childkeys": [
    "modifiers",
    "name",
    "parameters",
    "type",
    "body"
  ],
  "modifiers": [
    {
      "kind": 92,
      "kindStr": "ExportKeyword"
    }
  ],
  "name": {
    "kind": 78,
    "kindStr": "Identifier",
    "symbolref": {
      "id": -1,
      "name": "fn_record",
      "flagStrings": [
        "Function"
      ],
      "flags": 16
    }
  },
  "parameters": [
    {
      "kind": 160,
      "kindStr": "Parameter",
      "childkeys": [
        "name",
        "type"
      ],
      "name": {
        "kind": 78,
        "kindStr": "Identifier",
        "symbolref": {
          "id": -1,
          "name": "a",
          "flagStrings": [
            "FunctionScopedVariable"
          ],
          "flags": 1
        }
      },
      "type": {
        "kind": 173,
        "kindStr": "TypeReference",
        "childkeys": [
          "typeName",
          "typeArguments"
        ],
        "typeName": {
          "kind": 78,
          "kindStr": "Identifier",
          "symbolref": {
            "id": 1,
            "name": "Record",
            "flagStrings": [
              "TypeAlias"
            ],
            "flags": 524288
          }
        },
        "typeArguments": [
          {
            "kind": 147,
            "kindStr": "StringKeyword"
          },
          {
            "kind": 128,
            "kindStr": "AnyKeyword"
          }
        ]
      }
    }
  ],
  "type": {
    "kind": 113,
    "kindStr": "VoidKeyword"
  },
  "body": {
    "kind": 230,
    "kindStr": "Block"
  }
}


Read more here: https://stackoverflow.com/questions/65745804/typescript-ts-morph-resolving-parameter-type-in-export-function-fn-recorda

Content Attribution

This content was originally published by Craig Hicks at Recent Questions - Stack Overflow, and is syndicated here via their RSS feed. You can read the original post over there.

%d bloggers like this: