Repository: TypeScript
Issue: CallHierarchy.getOutgoingCalls does not detect a call of function defined via interface with call signatures
Labels: Bug,Help Wanted

# Bug Report

### 🔎 Search Terms

`callHierarchy`, `call hierarchy overload`

### 🕗 Version & Regression Information

`typescript@4.1.3`

### ⏯ Playground Link

[Codesandbox link](https://codesandbox.io/s/cool-gareth-13czr?file=/src/index.js)
(not sure how to showcase this in the TS Playground)

### 💻 Code

*package.json*
```
...
"scripts": {
  "start": "node index.js"
},
"dependencies": {
  "@types/react": "17.0.0",
  "@types/react-dom": "17.0.0",
  "react": "17.0.1",
  "react-dom": "17.0.1",
  "typescript": "4.1.3"
},
...
```

*index.js*
```
let program = ts.createProgram(["./ReactApp.tsx"], {
  baseUrl: ".",
  module: ts.ModuleKind.ESNext,
  moduleResolution: ts.ModuleResolutionKind.NodeJs,
  esModuleInterop: true,
  target: ts.ScriptTarget.ES5,
  jsx: ts.JsxEmit.React
});

const outgoingCalls = ts.CallHierarchy.getOutgoingCalls(
  program,
  program.getSourceFile("./ReactApp.tsx")
);

const output = JSON.stringify(
  outgoingCalls.map((call) => ({
    toFile: call.to.file,
    toName: call.to.name
  })),
  null,
  4
);

console.log(output);
```

*ReactApp.tsx*
```
import React from "react";
import ReactDOM from "react-dom";

ReactDOM.render(<div>123</div>, document.getElementById("root"));
```

### 🙁 Actual behavior

Only a single outgoing call is printed for the *ReactApp.tsx*:
```
[
    {
        "toFile": "/sandbox/node_modules/typescript/lib/lib.dom.d.ts",
        "toName": "getElementById"
    }
]
```

### 🙂 Expected behavior

I'd expect that a call to `render` method of `ReactDOM` would be reflected in the outgoing calls as well.

<!-- What you expected to happen instead, and why -->

### Research

The ReactDOM's `render` method is defined in the following way inside the `node_modules/@types/react-dom/index.d.ts`:
```
export as namespace ReactDOM;
...
export const render: Renderer;
...
export interface Renderer {
  // a bunch of call signatures go here
}
```

In other words, the `render` is represented via interface as an overloaded function. The `ReactDOM` is not the only widespread lib utilizing such definition approach. E.g. `connect` method of `react-redux` is defined in the similar way.

### Proposed solution

For such kind of definitions the `src/services/callHierarchy`.`isValidCallHierarchyDeclaration` returns `false`. Imo, that doesn't sound to be right. And it can be fixed by adding another check into the `isValidCallHierarchyDeclaration` which may look like:
```
function isDeclarationOfOverloadedFunction(typeChecker: TypeChecker, node: Node): boolean {
    if (node?.symbol) {
        const type = typeChecker.getTypeOfSymbolAtLocation(node.symbol, node);
        if (type?.symbol?.declarations) {
            const interfaceDeclarations: InterfaceDeclaration[] = type
                .symbol
                .declarations
                .filter(d => d.kind === SyntaxKind.InterfaceDeclaration) as InterfaceDeclaration[];
            if (interfaceDeclarations.length === type.symbol.declarations.length) {
                let members: TypeElement[] = [];
                interfaceDeclarations.forEach(decl => {
                   members = [...members, ...decl.members];
                });
                return members.length > 0 && members.filter(m => m.kind !== SyntaxKind.CallSignature).length === 0;
            }
        }
    }
    return false;
}
```

Here you can find a branch with the full changeset (and a new test) that allows detecting the ReactDOM's `render` call - https://github.com/microsoft/TypeScript/compare/master...pavgra:call-hierarchy-call-to-overloaded-function?expand=1. Happy to raise a PR if the report is confirmed to be valid and the changeset looks fine to the stakeholders.