microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
101.23k stars 12.52k forks source link

[Compiler API] function which check if a class implements an interface #35900

Open ajaxlab opened 4 years ago

ajaxlab commented 4 years ago

Search Terms

Compiler API, InterfaceDeclaration, ClassDeclaration, Checker

Suggestion

A function to check whether a ClassDeclaration implements an InterfaceDeclaration

Use Cases

Hello, I wanted to check whether a ClassDeclaration of a.ts file implements an InterfaceDeclaration from b.ts file using Compiler API. But I couldn't find a method or a function for it.

Examples

function isClassImplementInterface(
  ts.ClassDeclaration: classDeclaration,
  ts.InterfaceDeclaration: interfaceDeclaration
): boolean {
  // return true if classDeclaration implements interfaceDeclaration correctly
}

Checklist

My suggestion meets these guidelines:

ajafff commented 4 years ago

I don't think this should be part of the public API. The more API surface there is, the more the TS team has to maintain.

The current API provides everything you need to write your own utility function for this check (untested, but should work):

function classImplementsInterface(classDeclaration: ts.ClassDeclaration, interfaceDeclaration: ts.InterfaceDeclaration, checker: ts.TypeChecker) {
    const heritageClause = classDeclaration.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ImplementsKeyword);
    if (!heritageClause) {
        return false;
    }
    const interfaceSymbol = checker.getSymbolAtLocation(interfaceDeclaration.name);
    if (!interfaceSymbol) {
        return false;
    }
    return heritageClause.types.some((baseType) => {
        let symbol = checker.getSymbolAtLocation(baseType);
        if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
            symbol = checker.getAliasedSymbol(symbol);
        }
        return symbol === interfaceSymbol;
    });
}

If you need help to understand what it does, just ask here.

If I'm in the need for such functionality, I typically stuff it in my utility library tsutils which already contains a whole lot of useful (and mostly undocumented) functions.

ajaxlab commented 4 years ago

@ajafff Wow, Thanks a lot, the above code will be a great help. I'll try to make a utility code based on your guide. Thanks ! :D

ajaxlab commented 4 years ago

@ajafff I tested the above code. It works well, but I fixed a part.

getSymbolAtLocation(baseType) to getSymbolAtLocation(baseType.expression)

function classImplementsInterface(classDeclaration: ts.ClassDeclaration, interfaceDeclaration: ts.InterfaceDeclaration, checker: ts.TypeChecker) {
    const heritageClause = classDeclaration.heritageClauses?.find((clause) => clause.token === ts.SyntaxKind.ImplementsKeyword);
    if (!heritageClause) {
        return false;
    }
    const interfaceSymbol = checker.getSymbolAtLocation(interfaceDeclaration.name);
    if (!interfaceSymbol) {
        return false;
    }
    return heritageClause.types.some((baseType) => {
        let symbol = checker.getSymbolAtLocation(baseType.expression);
        if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
            symbol = checker.getAliasedSymbol(symbol);
        }
        return symbol === interfaceSymbol;
    });
}

I hope I did right :D Thanks!