Skip to content

Private members used only via [] notation trigger --noUnusedLocals error #14137

Closed

Description

@studds

TypeScript Version: 2.1.1 / nightly (2.2.0-dev.201xxxxx)

Version 2.1.6

Code

class PhoneBook {
   private sally: number = 123; // error TS6133: 'sally' is declared but never used.
   private sam: number =456; // error TS6133: 'sam' is declared but never used.
   getNumber(who: 'sally' | 'wendy'): number {
       return this[who]; // tsc is able to correctly deduce that this is a number
   }
}
const phoneBook = new PhoneBook();
console.log(phoneBook.getNumber('sally'));

Expected behavior:

Using a private member via [] notation should not trigger --noUnusedLocals errors.

Actual behavior:

A private member used only via [] triggers --noUnusedLocals errors.

Activity

  1. studds commented on Feb 17, 2017

    @studds
    Author

    It could reasonably be argued that this is not an obvious or perhaps common use case. And indeed, this can be worked around. Obviously, this example is contrived in the extreme. And yet, I would maintain that in some cases it may be a reasonable use case which obeys (I believe) all the rules of the language, and ought not trigger a compiler error.

  2. kemsky commented on Mar 29, 2017

    @kemsky

    It does not work in a more simple case like object["property"].

  3. mysticatea commented on May 21, 2017

    @mysticatea

    I have encountered this issue. In my case, I use state property for dynamic dispatching.
    Can I avoid those errors and keep dynamic dispatching?

    type ParserState = "A_STATE" | "B_STATE" | "Z_STATE"
    
    class Parser {
        private state: ParserState
    
        constructor() {
            this.state = "A_STATE"
        }
    
        parse() {
            while (this.state !== "Z_STATE") {
                this.state = this[this.state]()
            }
        }
    
        private A_STATE(): ParserState { /* .... */ } // 'A_STATE' declared but never used.
        private B_STATE(): ParserState { /* .... */ } // 'B_STATE' declared but never used.
        private Z_STATE(): ParserState { /* .... */ } // 'Z_STATE' declared but never used.
    }
  4. Manusan42 commented on Jul 1, 2017

    @Manusan42

    I have a similar problem like mysticatea...

    @injectable()
    export class MasterHandler {
      @inject(TypeKeys.AHandler) private _aHandler: Types.AHandler;
      @inject(TypeKeys.BHandler) private _bHandler: Types.BHandler;
    
      async process(request: Request): Promise<Response> {
        return await this[`_${request.scope}Handler`][request.action](request.payload);
      }
    }

    However, getting forced to remove some intentional unused locals like specific imports, I decided to use tslint till tsc supports warning messages. Besides that I just agree with alexeagle who said in issue #15953 :

    So I believe TypeScript should avoid non-fatal diagnostics (and unused variable was a mistake).

    To overcome the need of activated type info for tslint core rule no-unused-variable I came up with ajafff's tslint-consistent-codestyle custom rule no-unused which workes quiet fine (without type info) for many cases, just class members are ignored for now, but it's a brand new rule, so let's wait and see.

  5. added
    Design LimitationConstraints of the existing architecture prevent this from being fixed
    BugA bug in TypeScript
    Effort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".
    and removed
    BugA bug in TypeScript
    Design LimitationConstraints of the existing architecture prevent this from being fixed
    on Apr 12, 2018
  6. added this to the milestone on Apr 12, 2018
  7. Dervol03 commented on May 17, 2018

    @Dervol03

    +1

    I opt for improving this as well, because the the dynamic dispatching (as named by Toru Nagashima (@mysticatea)) is a very efficient way of implementing the strategy pattern in TypeScript that I use a lot, as it removes the necessity of an extra object only existing to map a string to a certain function.

    Here is an example of a class converting a JSON schema into a valid elasticsearch mapping, that only serves the purpose of demonstration, in a real world, I wouldn't actually implement it as a class.

    Example:

    class JsonToEsConverter {
      private _esMapping: EsMap = {}
    
      constructor(private _schema: JSONSchema) {}
    
      public get esMapping(): EsMap {
        if (_.isEmpty(this._esMapping)) {
          this.buildEsMapping();
        }
        return this._esMapping;
      }
    
      private buildEsMapping(): void {
        for (const prop in this._schema.properties) {
          this._esMapping[prop] = this[`${_schema.properties[prop]}ToEsMap](_schema.properties[prop]);
        }
      }
    
      private stringToEsMap(stringProp..){}
      private integerToEsMap(intProp..){}
      private arrayToEsMap(arrayProp...) {}
    }

    This is actually nice and concise. However, the compiler does not understand, that the -ToEsMap methods are actually used and throws an error.
    Which forces me to either extract them into a separate module, which violates the separation of concerns principle and considerable augments the number of arguments the methods would have to take, or, to "manually" create an additional map property to hold these methods:

    class JsonToEsConverter {
      private _esMapping: EsMap = {};
      private _converterMap: {[converterName: string]: Function};
    
      constructor(private _schema: JSONSchema) {
        this._converterMap = {
          stringToEsMap: this.stringToEsMap.bind(this);
          integerToEsMap: this.integerToEsMap.bind(this);
          arrayToEsMap: this.arrayToEsMap.bind(this);
        }
      }
    
      public get esMapping(): EsMap {
        if (_.isEmpty(this._esMapping)) {
          this.buildEsMapping();
        }
        return this._esMapping;
      }
    
      private buildEsMapping(): void {
        for (const prop in this._schema.properties) {
          this._esMapping[prop] = this[`${_schema.properties[prop]}ToEsMap](_schema.properties[prop]);
        }
      }
    
      private stringToEsMap(stringProp..){}
      private integerToEsMap(intProp..){}
      private arrayToEsMap(arrayProp...) {}
    }

    Which doesn't really help. Or worse, move everything to static methods, which creates the same problems as the ones with a separate module

  8. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptEffort: ModerateRequires experience with the TypeScript codebase, but feasible. Harder than "Effort: Casual".Help WantedYou can do this

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions