dsherret / dsherret/ts-morph

`node.getDefinitionNodes()` fails to find anonymous default exported function or class.

Open
#1,301 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
6.2k
Forks
238
Avg merge
2m
Merged PRs (30d)
1

Description

**Describe the bug**
Calling `node.getDefinitionNodes()` on an identifier node who's corresponding definition is a default exported anonymous function or class returns an empty array. It should instead return the declaration node as it does if the class or function is anonymous.

When calling `node.getDefinitions()` it returns an array with a single `DefinitionInfo` node so the issue appears to exist in `getDefinitionNodes` implementation. (`getDefinitions` is from typescript `getDefinitionNodes` is from ts-morph).

Version: 15.1.0

**To Reproduce**

```ts
import { Project } from "ts-morph";

const project = new Project();
const sourceFile = project.createSourceFile("fileWithDefaultUnamed.ts", `export default function() {}`);
const sourceFile2 = project.createSourceFile("test.ts", `import unnamedFn from "./fileWithDefaultUnamed.ts";
unnamedFn()`);

const callExpr = sourceFile2.getFirstDescendantByKind(SyntaxKind.CallExpression);
// The following will return an empty array
const definitionNodes = callExpr?.getExpressionIfKind(SyntaxKind.Identifier)?.getDefinitionNodes();
// The following will return an array with a single entry
const definitions = callExpr?.getExpressionIfKind(SyntaxKind.Identifier)?.getDefinitions();
```
A similar example can be cooked up for a unnamed class.

**Expected behavior**
It should instead return the declaration node as it does if the class or function was named.

**Possible solution**
The problem appears to be in the implementation of [`getDefinitionNodes`](https://github.com/dsherret/ts-morph/blob/cea07aa7759ecf5a1e9f90b628334b8bd617c624/packages/ts-morph/src/compiler/tools/results/DefinitionInfo.ts#L52) as it looks for matching identifier node which won't exist since the declaration doesn't have an identifier due to being anonymous.

I have got around this myself by doing the following instead of `node.getDefinitionNodes()`:
```ts
const definitions = node.getDefinitions();
const definitionNodes = definitions
.map((d) => d.getNode().getParent())
.filter(isNotUndefined);
```

Not sure if there are cases where this won't work but it seems like it should work. Could the implementation linked above be changed to this method?

Contributor guide

Open the contributing guide

Research direction

Start in packages/ts-morph/src/compiler/tools/results/DefinitionInfo.ts, at the getDefinitionNodes implementation linked in the issue. Compare its handling with getDefinitions and use the provided default-exported anonymous function reproduction as the first case to inspect. Done means getDefinitionNodes returns the declaration node for anonymous default-exported functions and classes, matching the expected behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.