Typescript: Show unused public properties and methods

Created on 5 Jan 2019  ·  9Comments  ·  Source: microsoft/TypeScript

It would be nice to have unused detection expanded, capable of detecting unused methods, exports, etc.

Code Example

_Shape.js_

class Shape {
    constructor() {
        this.color = 'red';
    }

    colorLog() {
        console.log(this.color);
    }
}

export default Shape;

_Circle.js_

import Shape from "./Shape";

class Circle extends Shape {
  constructor() {
    super();

    // FIXME: description should show as unused in VSCode, as it does in WebStorm.
    this.description = 'A circle shape';
  }

  // FIXME: circleLog should show as unused in VSCode, as it does in WebStorm.
  circleLog() {
    // NOTE: both VSCode and WebStorm have detected an unused variable.
    const num = 2;

    // NOTE: colorLog is being used, so no unused code errors in Shape.js file.
    super.colorLog();
  }
}

// FIXME: export should show as unused in VSCode, as it does in WebStorm.
export default Circle;

VSCode Screen

screen shot 2019-01-04 at 9 23 48 pm

WebStorm Screen

screen shot 2019-01-04 at 9 23 07 pm

In Discussion Suggestion

Most helpful comment

I am also interested to remove the kind of dead code mentioned by zpdDG4gta8XKpMCd .

Maybe Patricio Zavolinsky (pzavolinsky), creator of https://www.npmjs.com/package/ts-unused-exports, can help the Visual Code team to implement it?

All 9 comments

We currently assume that exported members form a public API and therefore are always used. We could try to detect that a certain set of files are an API entrypoints, and then mark unused internal exports. Not sure if there are any issue already tracking this

what about exported but unused

  • modules
  • classes
  • types
  • interfaces/props
  • members (enum | union)
  • functions
  • constants/variables?

is there any news about this issue?

Update: much faster after adding dummy getProjectVersion implementation assuming the project doesn't change during execution.

My naive solution below. It's pretty slow even though it should only run a full compilation once. I'll post updates if I improve it.

const fs = require("fs");
const ts = require("typescript");
const path = require("path");

// Copied from https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API#incremental-build-support-using-the-language-services
function getLanguageService(rootFileNames, options) {
    const files = {};

    // initialize the list of files
    rootFileNames.forEach(fileName => {
        files[fileName] = { version: 0 };
    });

    // Create the language service host to allow the LS to communicate with the host
    const servicesHost = {
        getScriptFileNames: () => rootFileNames,
        getScriptVersion: fileName => files[fileName] && files[fileName].version.toString(),
        getScriptSnapshot: fileName => {
            if (!fs.existsSync(fileName)) {
                return undefined;
            }

            return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName).toString());
        },
        getCurrentDirectory: () => process.cwd(),
        getCompilationSettings: () => options,
        getDefaultLibFileName: options => ts.getDefaultLibFilePath(options),
        getProjectVersion: () => 1,
        fileExists: ts.sys.fileExists,
        readFile: ts.sys.readFile,
        readDirectory: ts.sys.readDirectory,
    };

    // Create the language service files
    const services = ts.createLanguageService(servicesHost, ts.createDocumentRegistry());

    return services;
}

const tsconfigPath = "tsconfig.gen.json";
const basePath = path.resolve(path.dirname(tsconfigPath));
const parseJsonResult = ts.parseConfigFileTextToJson(tsconfigPath, fs.readFileSync(tsconfigPath, { encoding: "utf8" }));
const tsConfig = ts.parseJsonConfigFileContent(parseJsonResult.config, ts.sys, basePath);
const services = getLanguageService(tsConfig.fileNames, tsConfig.options);

// For each non-typings file
tsConfig.fileNames
    .filter(f => !f.endsWith(".d.ts"))
    .forEach(file => {
        const source = ts.createSourceFile(file, fs.readFileSync(file, { encoding: "utf8" }));
        ts.forEachChild(source, node => {
            if (ts.isClassDeclaration(node)) {
                // For each class member
                node.members.forEach(member => {
                    // If member is marked as public or protected and not a constructor
                    if (
                        (ts.getCombinedModifierFlags(member) & ts.ModifierFlags.Public ||
                            ts.getCombinedModifierFlags(member) & ts.ModifierFlags.Protected) &&
                        member.kind !== ts.SyntaxKind.Constructor
                    ) {
                        const references = services.findReferences(file, member.name.pos + 1);
                        // Fail if every reference is a definition and not in a typings file
                        if (
                            references.every(
                                reference =>
                                    reference.references.length === 1 &&
                                    reference.references[0].isDefinition &&
                                    !reference.definition.fileName.endsWith(".d.ts")
                            )
                        ) {
                            console.error(`File: ${file} , Member: ${member.name.text}`);
                        }
                    }
                });
            }
        });
    });

I was wondering if we could use custom rules to detect and fix this kind of problem

I would like this because using top-level exports, and destructuring imports can clutter your files quite a lot. E.g. I would like to make a Util class I can use the default import on, without worrying about forgetting to delete unused functions a year down the road.

Or would namespaces be a solution to this problem? (does unused code detection work on namespaces?)

I am also interested to remove the kind of dead code mentioned by zpdDG4gta8XKpMCd .

Maybe Patricio Zavolinsky (pzavolinsky), creator of https://www.npmjs.com/package/ts-unused-exports, can help the Visual Code team to implement it?

Another vote for this feature, first priority IMO would be some kind of indication for unused variables - grey/red squiggles, don't care much.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

weswigham picture weswigham  ·  3Comments

uber5001 picture uber5001  ·  3Comments

Antony-Jones picture Antony-Jones  ·  3Comments

manekinekko picture manekinekko  ·  3Comments

fwanicka picture fwanicka  ·  3Comments