dsherret / ts-morph

TypeScript Compiler API wrapper for static analysis and programmatic code changes.
https://ts-morph.com
MIT License
4.98k stars 195 forks source link

JSDoc.getTags()[0].getTagName() fails on @template tag #779

Open abirmingham opened 4 years ago

abirmingham commented 4 years ago

Describe the bug typescript version: 3.7.4 ts-morph version: 6.0.2

Here's an odd one! I am unable to call getTagName() on JSDoc tags if they are @template tags. Oddly, I have a number of other tag names in my codebase, several of which are nonstandard, and yet @template is the only offender.

To Reproduce

import { Project, SyntaxKind, ts } from 'ts-morph';

console.log(ts.version); // 3.7.4

const project = new Project();
const file = project.createSourceFile(
    'src/tmp.ts',
    [
        '/** @template Thing this is a thing */',
        'const printThing = (thing: Thing): Thing => {',
        '  console.log(thing);',
        '  return thing;',
        '};',
    ].join('\n'),
);

// Print all tag names
file.getFirstDescendantByKindOrThrow(SyntaxKind.VariableStatement)
    .getJsDocs()
    .forEach(jsDoc => {
        jsDoc.getTags().forEach(tag => {
            console.log(tag.getTagName()); // TypeError: tag.getTagName is not a function
        });
    });

Expected behavior Console prints @template.

Actual behavior TypeError: tag.getTagName is not a function

In my case I am able to work around the issue, but it is a bit of a nuisance (and a curiosity!)

dsherret commented 4 years ago

Hey Alex!

I didn't look into this yet, but I think the reason this is happening is because this tag is a JSDocTemplateTag:

image

And that node hasn't been wrapped yet (see here under "not exist").

Thanks for reporting and providing the simple reproduction. I'll look into implementing this soon.

abirmingham commented 4 years ago

@dsherret thanks for the quick reply! That does make sense.

Looking at wrapped-nodes.md, it looks like there might be more cases of this than JSDocTemplateTag, e.g. JSDocAuthorTag. Thinking out loud here, but I wonder if JSDoc.getTags() return type should become a union that includes the unwrapped nodes? Perhaps a union type wouldn't provide the optimal ergonomics, but it would be nice if the types somehow guaranteed that this issue would be caught at compile time rather than run time.