microsoft/TypeScript

Suggestion: Add the nameof compile-time operator to convert property and function names into strings

Taytay opened this issue Β· 175 comments

I would like to see the nameof operator be considered for Typescript.

This feature was just added to C# description, and it is an elegant solution to a common issue in Javascript.

At compile time, nameof converts its parameter (if valid), into a string. It makes it much easier to reason about "magic strings" that need to match variable or property names across refactors, prevent spelling mistakes, and other type-safe features.

To quote from the linked C# article:

Using ordinary string literals for this purpose is simple, but error prone. You may spell it wrong, or a refactoring may leave it stale. nameof expressions are essentially a fancy kind of string literal where the compiler checks that you have something of the given name, and Visual Studio knows what it refers to, so navigation and refactoring will work:

(if x == null) throw new ArgumentNullException(nameof(x));

To show another example, imagine you have this Person class:

class Person {
    firstName: string
    lastName: string
}
var instance : Person = new Person();

If I have an API that requires me to specify a property name by string (pretty common in JS), I am forced to do something like this:

   someFunction(personInstance, "firstName");

But if I misspell firstName, I'll get a runtime error.
So this is the type-safe equivalent:

   someFunction(personInstance, nameof(Person.firstName));

See also #394 and #1003.

A fix for the magic string problem is a common requirement on the forums. Hoping to see an official proposal for this soon.

Yeah, closing this as a dupe, suffice to say we definitely get the pain people feel here.

I don't see this as dupe of any mentioned issues. This could be critical to support advanced minification together with metaprogramming. Currently there is no way how to get member string after minification.
Though I don't have a clue how to add it to language without conflicting with other features...

In C# this works other way - you get unminified string. In Typescript I would need minified string. So maybe this could be actually different proposal :-) As unminified would be good too. Just some thoughs...

Agree. Doesnt look like a duplicate. A nameof operator should be considered as a very good idea. Probably "easy" to implement too.
I use typescript with angularjs and I like to create static string constants inside my controller/directive classes that I use when I register these classes in an angular module. Instead of hardcoding these names I would like to use nameof(MyDirectiveClass) instead.
Its the same kind of problem that we experienced earlier with PropertyChanged events in WPF (before nameof/CallerMemberName) when you renamed stuff.

Good point -- the other issues don't quite cover what this is talking about

πŸ‘

I'm using Typescript in a project at work, and am putting in a run-time duck type checker. The use case is de-serializing URL parameters.

If this feature was in the language, this is how I would expect the syntax to work:

class Foo {
    prop: string;
}

function isFoo(obj: Foo): boolean;
function isFoo(obj: any) {
    return (typeof obj === 'object') && 
              (obj !== null) && 
              (nameof(Foo.prop) in obj);
}

// Output for isFoo()
function isFoo(obj: any) {
    return (typeof obj === 'object') && 
              (obj !== null) && 
              ('prop' in obj);
}

Is that a correct assumption?

@frodegil , that would be an awesome use case. Would reduce a lot of code-smell and repetitive copy/pasting from my daily work-flow.

@Bobris, one would hope that the minified strings for the purposes of my example, I think if minification were to make its way into the tsc, this would be desired behavior.

@bryanerayner , your assumption is correct. The nameof operator is only used during compilation to convert type information into a string.

module pd {
   export class MyAngularControllerClass {
      public static IID : string = nameof(MyAngularControllerClass);  // "MyAngularControllerClass"
      public static $inject: string[] = [Model1.IID, Model2.IID, "$scope"];
      constructor(model1: Model1, model2:Model2, $scope:angular.IScope) {
      }
      public get nameOfThisGetter() : string {
         return nameof(this.nameOfThisGetter);    // "nameOfThisGetter";
      }
   }
   angular.module(nameof(pd)).controller(MyAngularControllerClass.IID, MyAngularControllerClass);
   // angular.module("myapp").controller("OldName", MyAngularControllerClass); < out of sync
}

Its so easy to get these hardcoded strings out-of-sync during refactoring

This would be fantastic.

Fantastic indeed. So much could be done with such an operator.

We want to see how things 'feel' once #394 and #1003 land. It's possible we can solve these use cases in the type system without having to resort to adding new expression-level syntax.

In the meantime it'd be good to collect any use cases that wouldn't be adequately addressed by those proposals.

Well, the case partially described by @frodegil about managing components that are registered somehow with a name (that most of the times is the same name of the Function/Class that is registered), as is the case with Angular, is a case that, as far as I can see, cannot be handled by either #394 's memberof nor by string literal types from #1003 (I'm really looking forward to the later though!).

I agree and understand how adding a new sintax element is undesired and even goes against typescript's main goal (to be a thin superset of javascript), but I still consider this proposal a very nice one.

Another small case

I use pouchdb and I make my own doc IDs, for performance and consistency reasons. These docIds are generated using a few fields, and one of them is based on the name of the model being saved, like:

For now I need to maintain a property called className on all my models:

class Person {
  className = 'Person';

  _id: string;
  $_dateCreated: number;
}

var p:Person;
p._id = `${this.className}_${window.appMetadata.cordovaUuid}__${this.$_dateCreated}__${window.uuid.v4()}`
pouchdb.put(p)

which could be turned into:

class Person {
  _id: string;
  $_dateCreated: number;
}

var p:Person;
p._id = `${nameof Person}_${window.appMetadata.cordovaUuid}__${this.$_dateCreated}__${window.uuid.v4()}`
pouchdb.put(p)

Foreseeable issues

I can see it becoming a bit harder to figure out the nameof of some things when dealing with generics or inheritance, so I will agree that this feature needs a lot of thought.

Thin alternatives

Considering that all cases that deal with property names can be handled completely by either #394 or #1003, I'd say the exposure of a getClass() and getClassName() (see example here) would solve the remaining cases I know of without needing to create new sintax elements. It could just as well be a core decorator for example.

A use case where neither #394 , #1003 ,getClass() nor GetClassName() would help would be this in Angular2:

    @Component({
        selector: 'my-selector',
        template: `<h1>{{${nameof MyComponent.prototype.title}}}</h1>`
    })
    class MyComponent { 
        public title = "Hellow world";
    }

It would makes the code refactoring proof, but it also makes it a lot less readable, so I'm not sure if is such a good idea. Just a thought.

Adding suggestion here to keep Enums in mind for nameof, as a shortcut for toString'ing an enum:

nameof(MyModule.Enums.FooEnum.Bar) === "Bar"

would compile to:

MyModule.Enums.FooEnum[MyModule.Enums.FooEnum.Bar] === "Bar"

or just:

"Bar" === "Bar"

A good use case is for unit tests where SinonJS is used for spying/stubbing. With the proposed nameof keyword, it's possible to pass the names of the methods and properties to be spied or stubbed. When method names are refactored, the unit tests won't break because of a string that needs to be updated.

var obj = new MyClass();
sinon.stub(obj, 'methodName', () => null); // Prone to break.
var obj = new MyClass();
sinon.stub(obj, nameof(MyClass.methodName), () => null);

Or, if the stub would support to pass in a predicate to resolve the name:

var obj = new MyClass();
function stub<T>(targetObject: T, targetMethodPredicate: (T)=>string, methodStub: Function){}
sinon.stub(obj, x => nameof(x.methodName), () => null);

I have a use case for constructing SQL strings. Here is a code snippet:

interface User {
    id: string;
    name: string;
    birthday: Date;
}

// Current implementation
var sql = `SELECT id,name FROM Users`;

// Desired implementation
var sql = `SELECT ${nameof(User.id)},${nameof(User.name)} FROM ${nameof(User)}s`;

The nameof() feature would make this type safe and refactor safe. For example renaming the User table to Account would be a quick refactor rather than searching through the code and missing dynamic strings like this.

I agree this should be a compile time transformation like it is in C# so we can burn in the type name.

@RyanCavanaugh
Five months later:
#1003 has landed but doesn't help in many scenarios (see below).
#394 is closed in favour of #1295. The latter seems nowhere near landing and while it solves an interesting part of the problem (typing) it doesn't help with the string/nameof part.

Maybe time to revisit?

Strings are bad because you can't "Find All References" them, you can't "Refactor/Rename" and they are prone to typos.

Some examples, using Aurelia:

let p: Person;

// Observe a property for changes
let bindingEngine: BindingEngine;
bindingEngine.observeProperty(p, 'firstName')
             .subscribe((newValue: string) => ...);

class Person {
  firstName: string;

  // Declare a function to call when value changes
  @bindable({ changeHandler: 'nameChanged'})
  lastName: string;

  nameChanged(newValue: string, oldValue: string) { ... }

  // Declare computed property dependencies
  @computedFrom('firstName', 'lastName')
  get fullName() {
    return `${firstName} ${lastName}`;
  }
}

// Declare dependencies for validation
validation.on(p)
  .ensure('firstName').isNotEmpty()
  .ensure('fullName', config => config.computedFrom(['firstName', 'lastName'])).isNotEmpty();

There you have five different APIs that would all benefit from having a nameof operator.

Valid points. I'll bring it up again (note: we're very busy right now with some upcoming work so suggestion triage is backlogged a bit)

Sorry I "Ctrl-Entered" my post too soon. I have added several real-life code examples that could benefit.

To make matters more complicated, some APIs above accept paths as well. So @computedFrom('p.address.city') is valid and it would be nice to support it as well...

In this comment in another issue I proposed some kind of limited Expressions from C#.

They would be a better alternative than nameof for most of the examples in this thread:

  • They can help type the result;
  • They support more complex member accesses (nested, indexers);
  • They give you a variable to start with, which in some case you won't have in scope with nameof.

Drawback of the last point is that Expressions would not cover cases such as:

function f(x: number) {
  let s = nameof(x);
}

But that's not as common as the other use-cases.

I am copying the description of the idea from the other issue:


Assume we have a special kind of strings to denote expressions.
Let's call it type Expr<T, U> = string.
Where T is the starting object type and U is the result type.

Assume we could create an instance of Expr<T,U> by using a lambda that takes one parameter of type T and performs a member access on it.
For example: person => person.address.city.
When this happens, the whole lambda is compiled to a string containing whatever access on the parameter was, in this case: "address.city".

You can use a plain string instead, which would be seen as Expr<any, any>.

Having this special Expr type in the language enables stuff like that:

function pluck<T, U>(array: T[], prop: Expr<T, U>): U[];

let numbers = pluck([{x: 1}, {x: 2}], p => p.x);  // number[]
// compiles to:
// let numbers = pluck([..], "x");

This is basically a limited form of what Expressions are used in C# for.

basarat/typescript-book#33 at least has some workarounds (although I just ran into an issue where there was no trailing ';' after my return due to ASP.Net's simple minification)

Big πŸ‘ πŸ’― from me for nameof support.

I'm using the workarounds for the reasons many others above have given - I want something refactor-friendly. APIs that accept strings to identify the name of some property on a Javascript object are plentiful.

Here's a real-world example where nameof could have been very useful.

I have a class that serves as a 'controller' to functionality that is executed remotely in a worker. The structure of the controller class mirrors exactly the actual implementation class, which is executed within the worker.

I need to safely reference the name of each method in the controller in a string, which would then be stored within the request message sent to the worker. However, the current best solution is not very type-safe:

export class LocalDBWorkerController<V> extends LocalDBBase<V> {
        initializeWorkerDB(options: BrowserDBOptions): Promise<void> {
            return this.executeInWorker("initializeWorkerDB", [options]);
        }

        getEntry(key: string): Promise<DBEntry<V>> {
            return this.executeInWorker("getEntry", [key]);
        }

        getEntries(keys: string[]): Promise<EntryArray<V>> {
            return this.executeInWorker("getEntries", [keys]);
        }

        getAllEntries(): Promise<EntryArray<V>> {
            return this.executeInWorker("getAllEntries");
        }

        // ...
}

If one of the method names is renamed, I need to remember to manually rename its string representation as well, or else I'll get a run-time error (assuming this code is thoroughly tested, otherwise it may be missed).

It would have been great if I could just write:

export class LocalDBWorkerController<V> extends LocalDBBase<V> {
        initializeWorkerDB(options: BrowserDBOptions): Promise<void> {
            return this.executeInWorker(nameof(this.initializeWorkerDB), [options]);
        }

        getEntry(key: string): Promise<DBEntry<V>> {
            return this.executeInWorker(nameof(this.getEntry), [key]);
        }

        getEntries(keys: string[]): Promise<EntryArray<V>> {
            return this.executeInWorker(nameof(this.getEntries), [keys]);
        }

        getAllEntries(): Promise<EntryArray<V>> {
            return this.executeInWorker(nameof(this.getAllEntries));
        }

        // ...
}

Thank you for re-opening this issue, because I would really like to see the nameof operator.

The nameof operator would be a compile time feature, so no impact on performance for the run-time, but we get save refactoring of property names and prevent "magic strings".

Discussed for a while and we think there's a better proposal that solves this use case.

The solution for this general problem space described in #1295 looks to be a better fit for TypeScript. If that proposal were correctly implemented, you'd still get the refactoring and validation benefits because the strings would be contextually typed and identified as property names (so rename / find references / go to def / etc would still work).

#1295 also provides better typechecking (because you couldn't nameof the wrong thing) while avoiding the compat problem (you may have a function named nameof already!) and the "what happens if ES7+ adds this operator too" problem.

You'd still also be able to have some good expression-level verification, too, because you could write e.g. <memberof MyType>"SomeMember" (which would be validated!) instead of nameof myTypeInstance.SomeMember. This is even more advantageous because now you don't need an instance of MyType to get its property names, which happens reasonably often.

<memberof MyType>"someMember"
  1. Doesn't look very intuitive or friendly to beginners (more abstract, requires understanding of what is a type cast, type modifier).
  2. Longer. Adds additional 'noise' (< >, " ").
  3. Isn't comfortably usable for anonymous types. E.g. let x = { prop1: 123 }; let s = nameof(x.prop1). Unless somewhat verbose syntax is used, E.g. <memberof typeof x>"prop1".
  4. Doesn't suggest a natural extension for nesting. E.g. like nameof(myTypeInstance.someMember.nestedMember).
  5. Doesn't allow for editor auto-completion.
  6. Doesn't provide a target for the rename operation itself (e.g. clicking it and pressing F2).
  7. Less informative error message: e.g. "someMember" is not assignable to... "member1" | "member2" | .. instead of the more accurate 'someMember' is not a member of ....
  8. Doesn't provide a way to exclude private or protected members (unless something like publicMemberof or publicOrProtectedMembersOf is provided, but that may seem a bit too verbose). I consider that very important functionality - this is the kind of safety that programmers need and nameof was designed to provide.

I think #1295 is interesting but seems at most complementary (they could work together nicely)? This seems like a strange decision (especially with the 'Declined' verdict-like tag which I feel is inappropriate here - and in general). I think nameof is a good syntax and concept. There is no need to excessively 'rationalize' or try to justify the decision not to implement it just because it may interfere with future EcmaScript syntax (which seems to be the primary concern here, I believe).

Re: points 1-4 In most cases, you're just write "someMember" and everything would just work. I'd only use the <...>" " syntax when the target didn't provide a memberof type, which is going to be somewhat rare.

Point 5 is correct.

Point 6 is not correct; you'd be able to rename in that position.

Point 7 is not correct when the target is memberof -- we'd be able to provide a good error message there, I think.

Point 8, I agree we'd need to figure that out.

especially with the 'Declined' verdict-like tag which I feel is inappropriate here - and in general

I'm not sure what the alternative is?

class Example {
   a: number
   protected b: string: 
   private c: boolean;
}

function getProp(obj: Example, prop: memberof Example) {
  //..
}

For 1-4 (in relation to 8) there is a limitation here, compared to the expected behavior with nameof. The limitation is that with nameof, depending on the context of the call, there would be a different expectation of which members to include as valid. If referenced from outside the class, memberof would be expected to only include "a", from a derived class it would be "a" | "b" and from the inside it would be "a" | "b" | "c". I don't think there is any way to elegantly 'model' that here, as a type is not usually contextually determined (though everything is possible? I guess, but I haven't had the time to really consider the consequences).

For 6. renaming through a string would appear a bit strange. I suppose it could be done but since it is not in a visible context (E.g. someInstance.member) it may not guard as well against human mistakes.


The Edge platform status page uses the terminology:

  • Supported
  • Preview Build
  • In Development
  • Under Consideration
  • Deprecated
  • Not Currently Planned

Based on that:

  • I think 'Unplanned' (also used in the VS Code GitHub as a Milestone) would be a good replacement (and more friendly) term for 'Declined': If the issue is closed it would mean it would 'probably never happen', if open, 'maybe one day, who knows'.
  • 'Under Consideration' could replace 'In Discussion' (which is somewhat confusing with the 'Discussion' tag).
  • 'Planned' and 'In Development' to replace 'Committed' perhaps.
  • 'Duplicate' is sometimes used for suggestions where a different approach was preferred. These are not really 'duplicates', something like 'Merged' may work better (there might be a better term but I can't think of anything right now, need more time). [Edit: perhaps something like 'Converged'? 'Merged' could be really confusing with Git 'Merge'].
  • Also 'Too Complex' -> 'Not Worthwhile' maybe.

I think some of these alternatives also better reflect the fact that this is not really a 'community' directed project (say like Node.js). Terms like 'In Discussion' may seem like this may be in discussion the 'community' rather than the design team (at least I thought that way at first). 'Declined' seems a bit extreme (never say never..). To me it feels like it characterizes the feature requests a bit like they were 'demands' or even 'trials'. Sometimes they are just 'ideas', thoughts, or rudimentary expressions of needs, points for investigation etc. Sometimes they may only serve as 'stages' or sources of information to better ones..

I'm willing to put some personal effort to work on these. Maybe I'll open a meta-issue when I'll have a more complete set of terms that I'd feel is good enough.

That's good feedback and it looks like we do have room for improvement here. I'm not a huge fan of "Unplanned" (it sounds like we neglected to plan for it?), but I think the list you have there is nice. I'd welcome the new list if you do come up with something that's less ambiguous and more friendly than what we have now.

@RyanCavanaugh

Yes, I realize "unplanned" could be also read as "unscheduled", so I'll need more time to think of something better. Anyway, I have more ideas (like perhaps splitting "By Design" to "Intended Behavior" and maybe something like "Design Limitation") but it's a bit off-topic here. I'm planning to take the complete set of tags that are listed in the FAQ, investigate their usage and try my best to find alternatives. I'll open a new issue when I get to a set of terms I feel is reasonable enough.

Here's an update for the best workaround for the use case I gave above. My current solution is to polyfill the ES6 name property for function instances, if needed, for all methods in the target object and in all of its prototypes:

(Version 5.0 of V8 doesn't require this polyfill when targeting ES6, I believe (in chrome 50 .name works for class methods) though in Node 6.0 some of the functionality is still behind a flag):

function polyfillMethodNameProperties(obj: any) {
    while (obj != null && obj !== Function.prototype && obj !== Object.prototype) {
        for (let propName of Object.getOwnPropertyNames(obj)) {
            let member = obj[propName];

            // Note: the latest Edge preview would resolve the name to 'prototype.[name]' 
            // so it might be better to force the polyfill in any case even if the 
            // feature is supported by removing '&& !member.name'
            if (typeof member === "function" && !member.name)
                Object.defineProperty(member, "name", {
                    enumerable: false,
                    configurable: true,
                    writable: false,
                    value: propName
                });
        }

        obj = Object.getPrototypeOf(obj);
    }
}

class LocalDBWorkerController<V> extends LocalDBBase<V> {
        constructor() {
            polyfillMethodNameProperties(this);
        }

        initializeWorkerDB(options: BrowserDBOptions): Promise<void> {
            return this.executeInWorker(this.initializeWorkerDB.name, [options]);
        }

        getEntry(key: string): Promise<DBEntry<V>> {
            return this.executeInWorker(this.getEntry.name, [key]);
        }

        getEntries(keys: string[]): Promise<EntryArray<V>> {
            return this.executeInWorker(this.getEntries.name, [keys]);
        }

        getAllEntries(): Promise<EntryArray<V>> {
            return this.executeInWorker(this.getAllEntries.name);
        }

        // ...
}

I'm not aware of a general purpose solution on how to apply this particular polyfill in a non-targeted way, though it successfuly solves the particular need I have.


A different, but more general approach can be implemented to support members having object types as well (but not members having primitive types like string, number, boolean, null or undefined as they cannot be compared by reference), and also allows for the reassignment of members. The approach I used here relies on an ES6 Map object, if available, to cache the list of properties:

let propertyNameCache: Map<any, string[]>;

function getAllPropertyNames(obj: any): string[] {

    let scanAllPropertyNames = (): string[] => {
        let propertyNames: string[] = [];

        while (obj != null) {
            Array.prototype.push.apply(propertyNames, Object.getOwnPropertyNames(obj));
            obj = Object.getPrototypeOf(obj);
        }

        return propertyNames;
    }

    if (typeof Map === "function") {
        if (propertyNameCache === undefined)
            propertyNameCache = new Map<any, string[]>();

        let names = propertyNameCache.get(obj);

        if (names === undefined) {
            names = scanAllPropertyNames();
            propertyNameCache.set(obj, names);
        }

        return names;
    }
    else {
        return scanAllPropertyNames();
    }
}

function memberNameof(container: any, member: any): string {
    if (container == null || (typeof container !== "function" && typeof container !== "object"))
        throw new TypeError("memberNameof only works with non-null object or function containers");

    if (member == null || (typeof member !== "function" && typeof member !== "object"))
        throw new TypeError("memberNameof only works with non-null object or function values");

    for (let propName of getAllPropertyNames(container))
        if (container[propName] === member)
            return propName;

    throw new Error("A member with the given value was not found in the container object or any of its prototypes");
}

Usage example:

class Base {
    dcba = {};
}

class Example extends Base {
    abcd = {};
}

let example = new Example();
console.log(memberNameof(example, example.abcd)); // prints "abcd"
console.log(memberNameof(example, example.dcba)); // prints "dcba"

(Note this would not detect addition of new members, unless the slower, uncached version is used - which rescans the whole prototype chain at every call. However in the realm of TypeScript there isn't really a need for that as the interface or class definition itself must be known at compile-time to provide the desired level of type safety, anyway)

@RyanCavanaugh The work on the alternative label suggestions is 95% done, I have covered almost all the current labels (this took many hours of work) - the most difficult label turned out to be the Declined label itself :) so this may take some additional weeks or even months.

Liero commented

<memberof MyType>"SomeMember"

This is even more advantageous because now you don't need an instance of MyType to get its property names

in C# you can write var propertyName = nameof(MyType.SomeMember)

it seems like both nameof and memberof makes sense. memberof is usefull for type annotation, nameof for getting name of object's members, variables, function parameters, classes, interfaces, namespaces, etc...

So i dont get it - will be there any kind of nameof operator or no? Because ticket is closed but comments keeps comming :)

I arrived here when I searched for: TypeScript CallerMemberName

Looking for something like C#'s [CallerMemberName] attribute. Not quite the same as nameof, but used in some cases to write a method so that the caller doesn't even have to use nameof; e.g., notifying that some property has changed, etc.

Is there a separate issue for that, or is it considered to be the same as this?

@DaveEmmerson This is a different topic. CallerMemberName can be found at run-time in javascript.

This issue is for a compile-time burn-in of a variable name, which is not available at run-time.

@styfle if you read that thread it can't actually be done at runtime consistently.

I'm talking compile time like CallerMemberName in C# and like nameof. I see them as having a similar mechanism, but I guess if this issue has reached its end then that would have a similar conclusion.

Was just a thought... :)

i don't quite get this reluctance either

nameof is merely a macros that transforms a property name identifier into a string literal at the syntax level, why is it such a big deal to add it?

I solved this problem with a babel plugin: https://github.com/enoshixi/babel-plugin-nameof

I've been using this in production sites for a few months and it's worked like a charm. Finally managed to get it up on github.

Can that babel plugin be installed to TS application and work together?

My use-case is a forms library I have developed. I would like to be able to use refactoring-compatible and compile-time-checked syntax such as the following:

// Press F2 to rename properties:
let myObj = { firstName: "John", enableTLAFeature: true, uglyPropName: "" }; 
let myForm = new Form( [ 
  new TextBox  ( myObj, nameof( myObj.firstName ), { required: true, maxLength: 20 } ), 
  new Checkbox ( myObj, nameof( myObj.enableTLAFeature ) ),
  new TextBox  ( myObj, nameof( myObj.uglyPropName ), { label: "Better Label" } )
] );

The component constructors take the object, the property name (as a string), and an optional options object.

Those form components would be able to read & write the property (using obj[propName] syntax) and also generate the display label automatically if none is provided in the options object (each component includes rendering of the label). I can convert the property name into a form label with a helper function. The function converts the first letter to upper case and inserts a space after every lower case letter when followed by an upper case letter and prior to any upper case letter followed by a lower case letter. For example, "firstName" becomes "First Name" and "enableTLAFeature" becomes "Enable TLA Feature". (If we need a better label, then it can be provided explicitly in the options object.)

Note: for nested objects, I just pass the nested object in the first parameter, such as myObj.address and I expect nameof(myObj.address.city) to be converted to "city" at compile-time (I am not doing property path navigation within the components, so I don't need complex expression support). With this approach, myObj, address, and city can all be renamed safely.

The above should be relatively easy to implement (just parse out the last symbol and put quotes around it). One disadvantage, though, is that I wouldn't have type safety for the properties, so I can't ensure that myObj.enableTLAFeature is a boolean. I'm not sure how to solve that, but I really prefer to have the call site specify the property only once (for purposes of reading, writing, and generating the label). If it were possible to pass just myObj.enableTLAFeature (as a boolean and without the nameof operator) and then use a callsitenameof operator within the component constructor (returning "enableTLAFeature" and not returning the constructor parameter name such as "propName"), then that would be fantastic, but I don't know if that would be possible.

Something like this could be very useful.

nameof<Foo> should appear (for all intents and purposes) as "Foo" that way the compiler / emitter can simply think of it as a string.

but nameof<T> that tracks what T actually is could be very useful for DI containers. For example:

registerConstant<T>(instance:T){
   this.container[nameof<T>] = instance;
}

then you can get around the interface issue for IOC:

binding:
ioc.registerConstant<IFoo>(new Foo());

injecting:

constructor(@inject(nameof<IFoo> foo:IFoo){... }

I created a small experimental library for doing this: https://github.com/dsherret/ts-nameof

It's still a work in progress. Let me know if you have any suggestions.

mmc41 commented

I also need a nameof macro. Unfortunately, the alternative "much smarter solutions" mentioned here does not seem to materialise any time soon. A simple nameof is better than something so complex to design that it never arrives or takes years.

Liero commented

@MeirionHughes: How would it translate into javascript?
I don't think it's much better than this very simple alternative:
ioc.registerConstant(nameof(IFoo), new Foo());

getting a name of a property or a function (original topic of the discussion) is not the same as getting a name of a type, consider:

  • nameof<[]> - what is the name of it?
  • nameof<{ coolStuff: MyType & MyOtherType & MyFavoritType | undefined | null | SomethingCompleteltyDifferent<Dictionary<string, Promise<Optional<PleaseStop>>[]>>; }>
  • nameof<string | number> vs nameof<number | string>

let along

  • nameof<string> vs nameof<"string">

this one is interesting too:

  • type N = number; nameof<number | N | number>

wait wait, here is more:

function helloEarthings<a, b, c extends a<b>>(one: number, two? = new class { hi = 'how are you?'  }, ...blah: MyMegaControl<typeof someOtherValueIJustMadeUp>[]) {
}
nameof<typeof helloEarhlings>
  • order of properties names:
nameof<{
    shouldWeGoLexical: 'hm'
    orAlphabetical: 'hm'
}>

@Aleksey-Bykov the answer to all of those is literally the type as a string; whether is of any use is a different matter - it gets typescript's types as a tangible string in js.

@MeirionHughes you understand that a type as written (in AST) is not the same as how it was read, stored internally and interpreted by TypeScript, don't you?

example:

const myVariable = 'hey';
type A = typeof myVariable
type B = 'hey';

nameof<A> === nameof<B> // <-- ???
nameof<typeof myVariable> === nameof<A> // <-- ???
nameof<'hey'> === nameOf<A> === nameof<typeof myVariable> // <-- ???
Liero commented

@Aleksey-Bykov: nameof should not work with anonymous expression by definition. You cannot get a name of something that does not have a name.

  • nameof(Array) -> "Array"
  • nameof([]) -> compiler error
  • let arr = []; nameof(arr) -> "arr"
  • nameof(string) -> "string"
  • nameof(String) -> "String"
  • nameof("string") -> compiler error
  • type N = number; nameof(N) -> "N"
  • nameof(number | N | number) -> compiler error
  • nameof({ coolStuff: MyType }) -> compiler error

however, this should work:

  • nameof(Array.length) -> "length"
  • let arr = []; nameof(arr.length) -> "length"
  • interface IPerson { firstName: string }; nameof(IPerson.firstName) -> "firstName";
  • let arr: Array<IPerson> nameof(arr[0].firstName) -> "firstName"

All of this has been already defined in C# and tested in real life and it works well.

I preffer nameof() notation over nameof<> in order to avoid conflict in JSX resp. TSX. This would be probably the most common scenario:

<input name={nameof(this.state.firstName)} />

@Liero, analogy with C# is hard to follow because, well, they are 2 different languages: C# doesn't have unions and intersection types as well as non-local anonymous types, for TS all this is bread and butter

it's not clear to me what the merit is for being able to get the last name at which a type was registered, there is only 40% cases in TS where such question even makes sense due to various anonymous type expressions

besides:

  • interface A { x: number; }
  • interface B extends A {}
  • type C = B;
  • type D = A;
  • type E = C | D

are all 100% same types in TS (thank to structural typing), how would i benefit from seeing them under different names (according to you)?

@Aleksey-Bykov
nameof() is not meant to be used on types, but on pointers mostly if not only. We don't care about the type of that pointer, I just want it's name.

const a: { what: string;}
const b: { what: number;}
const c: { what: any;}
const d: { what: undefined;}
const e: { what: never;}
const f: { what: {};}
const g: { what: typeof a;}
const h: { what: typeof b | typeof c;}
const i: { what: Whatever | You | Need | Ever;}

nameof(a.what) == 'what';
nameof(b.what) == 'what';
nameof(c.what) == 'what';
nameof(d.what) == 'what';
nameof(e.what) == 'what';
nameof(f.what) == 'what';
nameof(g.what) == 'what';
nameof(h.what) == 'what';
nameof(i.what) == 'what';

// they're all the same, regardless of the type

Now, if you're talking about asking the nameof() a type, then I agree with @Liero : the solution is to ignore or have a generic answer to any anonymous type expression, and the real actual answer to any well defined and named type reference. I don't think anyone will ever ask the name of a complex or fuzzy type, that has never passed my mind as a case for this feature.

I personally feel like the real value of nameof is not in getting type information at run time, but rather providing type safety when using string literals. As mentioned above, I'm currently using a babel plugin implementing nameof; here's a pretty typical use case for me (using React because that's what I'm currently working with so it's fresh in my mind, but the concept could be applied elsewhere):

interface FooBar {
  foo: string;
  bar: string;
}

interface Props {
  value: FooBar;
  onChange(value: FooBar) => void;
}

const FooBarControl = (props: Props) => {
  const handleChange = (event: React.FormEvent, name: string) => {
    const { value } = event.currentTarget as HTMLInputElement;
    const newValue = Object.assign({}, props.value, { [name]: value });
    props.onChange(newValue);
  };

  return (
    <div>
      <input value={props.foo} onChange={e => handleChange(e, nameof(props.foo))}/>
      <input value={props.bar} onChange={e => handleChange(e, nameof(props.bar))}/>
    </div>
  );
};

I can freely rename the foo or bar properties and nothing breaks, despite updating them using the property name as a literal string during runtime.

Liero commented

@Aleksey-Bykov: don't expect to get the type information from nameof operator. That would be a terrible mistake. Maybe type N = number; nameof(N) does not have real use cases, but for the sake of clarity and consistency, it makes perfekt sense. if(true){} also does not have real use case, but you can write it.

@fredgalvao you are talking about the names of the properties (not types) which is a whole different story that makes a lot of sense and i am with you on that

@Liero if you don't care about the type information, maybe we should limit it to the names of properties which aren't types, but rather value declarations because their name belongs to values which are there to stay in the emitted JS as opposed to types that get erased completely

there are 2 different domains that TS deals with:

  • value domain - everything that has to do with the emitted JS because every little thing in JS is an object
  • type domain - ephemeral things that help to reason about consistency of the code that aren't a part of the emitted JS

now

  • getting the names of values makes perfect sense because they mean things in JS
  • getting the names of types doesn't make much sense, because there is nothing in the resulting JS that has anything to do with them

except for... classes!

classes are in the twilight zone because they possess the properties of both values and types : they are values because of the prototype object and the constructor function, they are types because they govern a set of valid operations for its instances to be a part of

together with all trivial values like properties, functions, variables the classes should be a subject to nameof operator

Liero commented

@Aleksey-Bykov: That is subject for discussion, but I see use case for nameof with name of the class:

describe(nameof(Person)) {
   it(nameof(Person.sayHello) + `() should say "Hello!"`, function() {
      expect(new Person().sayHello()).toBe("Hello!");
   });
}

It allows for easier refactoring. Although the added value is relatively small, I don't see reason to limit it.

FYI, this is how C# works:

class A {}
nameof(A)
"A"
using B = A;
nameof(B)
"B"

No problem what so ever.

@Aleksey-Bykov names of properties is what I need for my use case, which I described in detail above. And other examples have been given that have the same requirement. We'd just like to be able to rename some symbols in our code (using refactoring tools) without breaking code that relies on those symbols at runtime (we don't want to hard code the symbols as strings).

@Liero there is no question about whether to let classes be a subject for nameof, because classes are values, so it's natural to let them be there, i see no problem

@YipYipX4 i hear you, properties were born for nameof, so worry no more, they will be nameable

getting the names of types doesn't make much sense, because there is nothing in the resulting JS that has anything to do with them

Which is entirely the point; interfaces don't have run-time presence at all... which is a problem, especially if you want to do inversion of control based on reflection.

take a look how inversify is solving the problem

basically if you have an interface IFoo you have to define the string "IFoo" somewhere so that you have an actual value when you get to javascript. Being able to have the interface name (emitted via something like nameof would be useful in making sure refraction doesn't break those symbols.

So given the inversify example, you could do:

public constructor(
  @inject(nameof(Katana)) katana: Katana,
  @inject(nameof(Shuriken)) shuriken: Shuriken
)

@MeirionHughes interfaces should not have runtime presence, interfaces are just types that were given names, types are all illusory entities whose only purpose is to assist in typechecking

now, what you wish you could do is to repurpose them by giving them additional responsibilities, namely to be bonds that hold together

  • a runtime expectation for a certain object and
  • a piece of code that fulfills this expectation by providing an expected object

interfaces in TS were not supposed to do that as designed, it's not what they do, they have 0 business in runtime

probably what confuses you is the name inteface and your C# background from where you have certain expecations of runtime behaviour, again C# and TS are very different although share some syntax and keywords

back to your injection framework it would work just as well if was using plain strings for linking implementations with places that depend on it:

@injectable('one-thing')
class A {}
@injectable('another-thing')
class B {}
class C {
   constructor(
     @inject('one-thing') katana: Katana,
     @inject('another-thing') shuriken: Shuriken
   ) {}
}

@Aleksey-Bykov here's a use case for interfaces.

Sure someone could put all this in constants, but if someone wants to use nameof with interfaces to keep their constant string values in line, I don't see a good reason to not allow them to do so.

@dsherret

i have looked at it already, thanks, using the name of the TS interface would not solve any problem, consider:

@injectable() 
class Broom extends Broom { sweep(): void {} }

@injectable()
class Katana extends Katana { cut(): void {} }

class Ninja{
    constructor(
        @inject(nameof(Broom)) katana: Katana // <-- did i get my sword yet?
    ) { }
}

questions:

  • what can nameof do to stop bad things like this from happening?
  • if it can't, how is it better than using plain strings "broom" like instead of nameof(Broom)?

@Aleksey-Bykov it would prevent you from doing "broom" instead of "Broom" ;)

On a serious note, I agree that it doesn't provide any extra protection over constants (as shown in the inversify example). Nameof for interfaces would just provide a way to let code be more maintainable by updating strings to interface names. That's really the only reason.

@dsherret before i say it's not worth the trouble, let's take it a bit further, so nameof is a new feature that can take an interface, in my ORM framework i have an interface interface IColumn<TValue> { }, now i need to do nameof(Column<number>) what will it give me? (hint, see my first comment today)

@Aleksey-Bykov I would say that the type args should be excludedβ€”nameof(Column<number>) should go to "Column". If someone wanted the type arg they could do: nameof(number);. That's what I did here. If that's not done, then there's no way to easily get the name of an interface with type args... or there needs to be an exception in the language to allow it to be written without the type arg (that would get nasty).

C#:

     nameof(A.B.C.D);  => "D"  // namespace A.B.C.D 
     nameof(A.B.C.D.IX); => "IX"  // interface IX in namespace A.B.C.D
     nameof(variableName); => "variableName";
     nameof(A.B.C.D.IX<int>); => "IX"  // interface IX<T> in namespace A.B.C.D;
     nameof(instance.Name); => "Name" // property Name of object instance

Typescript:

     nameof(A.B.C.D); => "D"  // module A.B.C.D
     nameof(A.B.C.D.IX); => "IX" // interface IX in module A.B.C.D
     nameof(variableName) => "variableName";
     nameof(A.B.C.D.IX<number>); => "IX" // interface IX<T> of module A.B.C.D
     nameof(instance.Name); => "Name" // property name of object instance

nameof strings can be calculated at compile-time and should be possible in Typescript as well.
This solves a lots of scenarios where string-names and type-names (1 to 1) is used in different JavaScript frameworks. This would make re-factoring so much easier (module names, controller names, dependency injections in angularjs, etcetera)
Why not make nameof work the same way as in C#?

@Aleksey-Bykov I agree with you that nameof() for types gets messy really easy, but I don't think we need to go there to provide a satisfatory solution/implementation that covers 99% of the scenarios.

Usual stuff

  • We already know and agree that properties/fields/variables are covered 100% (or expected to be).
  • We could limit nameof() on classes to get it's most immediate down-the-tree defined no-type-parameters name (Class<TypeParam<What<Is<Going | On>>>> -> "Class"). I mean, if you want it to be recognizable, just alias it:
type ClassFromHell = Class<TypeParam<What<Is<Going | On<I | DONT | EVEN>>>>>;

Groundbreaking thing

  • We could limit nameof() on compile-type-only type expressions (interfaces, abstract classes, inline definitions, type aliases, module definitions, etc) to behave in a simple and limiting way similar to what I suggested for classes above: just output the string inline where it's called.

Would it be the first thing that makes it through the compile void to runtime from TS? Maybe. But is it bad? I don't think so necessarily.

I really can't see nameof() being able to solve every need of the concept, so I won't even ask it to be fully featured. But holding the other 99% of the cases because of that 1% problematic doesn't seem like a good idea. We're not dealing with something that Ecmascript thinks about, so we don't need to strictly follow a draft or an specification, and as such we could accept this faulty version, which is 10^10*awesome to me already.

i am advocating the devil here, when you come with a proposal you'd better make sure you thought of all in/outs, you need to give a definitive answer to all possible cases (not interfaces only), be it compiler errors of defaults, you need to justify why it should work this way, you need to explain it to your users, you'd better be sure they like it, you need to allocate some resources for developing it and maintaining it, and you need to have a plan how you are going to build new features on top of that or prevent them from conflicting with it

now when the idea is simple and clear and the immediate benefits of it is obvious to everyone, that's one story

so would anyone say no? well, if we are talking about one special interface case, that only works without type parameters, and we don't really know what to do with all other types, because the concept simply makes no sense, let alone being intuitive, that seems like a problem already...

now if are we certain that this problem has to be solved, then we'd better have some real life examples that clearly demonstrate the benefits

so far there wasn't an good example as to why we need nameof for interfaces, besides unsupported statements about being so much easier to write code and maintain

I agree again with most of what you said. I wouldn't suggest or vote for a blurry feature. That's why I would love to see a well-defined clearly-limited simple version of this feature than to see no version at all.

I don't get how the examples posted here so far are considered "unsupported statements" though. Most of them are real world scenarios, specially IoC, used in many projects involving many different libraries and frameworks. Being able to write better code and maintain it with ease is the reason Typescript was created to begin with. We don't have HKT on it, for example, but we still have TS reaching v2.0 πŸ˜‰. I agree again*again that it won't solve every issue, but it'll solve a lot.

as far as interfaces (not properties, variables, classes which are out of question) i only saw one poor example and a few loud statements

essentially the problem that nameof is trying to solve for interfaces is to associate an interface with a string, so that:

  • anytime you have an interface you get a string
  • and the reverse problem of getting a type having a string

with a little inconvenience this can be done in plain TS as soon as today:

type Wrapper<Type> = string & { '': [Type] };
function toWrapperFor<Type>(name: string) : Wrapper<Type> { return <any>name; }
const associations = {
    one: toWrapperFor<string>('one'),
    another: toWrapperFor<MyMegaControl>('another')
};

// ... later in code

class MyClass<Type>{
    constructor(private wrapper: Wrapper<Type>) {}
    take(value: Type): void { /*...*/ }
    toString(): string { return this.wrapper; }
}

const oneInstance = new MyClass(associations.one); // MyClass<string>
oneInstance.toString(); // 'one'
const anotherInstance = new MyClass(associations.another); // MyClass<MyMegaControl>
anotherInstance.toString(); // 'another'

@Aleksey-Bykov the point is to have a string be guaranteed to be the same as an interface name. This is a minor benefit, but a benefit nonetheless. Your example doesn't do that. We already talked about the example with IoC and how it would be nice to have the strings refactor when refactoring the interface name.

To list out some reasons:

  1. It guarantees the strings will be the same as the interface name. This helps with the overall code quality in a small way.
  2. It saves a very small amount of time because when changing an interface name you don't also have to change the string... refactoring tools will do it for you.
  3. Using nameof helps show what the strings represent and because it shows what the strings represent, it allows us to quickly navigate to the interface definition using "go to definition". That helps with code clarity.

Those are the only reasons I can think of at the moment. These aren't loud or unsupported statements.

I would say for types that only interface and type alias types should be allowedβ€”no union/intersect/object/string/etc. types. Maybe even keywords (like number) shouldn't be allowed.

Liero commented

Again, don't reinvent the wheel. C# example:

class A<T> { }
nameof(A<string>)
"A"

when it comes to interfaces, pros and cons have been mentioned. No need to spam discussion anymore. There are @ahejlsberg and other great guys, they will make the right decision at the end of the day. Let's collect some more use cases

nameof is desperately needed if you, like me, use a NoSQL DB and do a lot of queries on string specified fields that loose refactoring, consistency, error-catching & auto-completion the moment you enter the quirky realm of Stringland.

this issue,open since 2014, really represents the saying: best is the enemy of better

--R

AngularJS example, configuring the DI:

angular.module("news", ["ngRoute"])
    .controller("NewsIndexController", App.News.NewsIndexController) // nameof

and referencing services:

    .config(($routeProvider: ng.route.IRouteProvider) => {
        $routeProvider
            .when("/news", {
                controller: "NewsIndexController", // nameof
                templateUrl: "modules/news/views/index.html",
                controllerAs: "vm",
            })
    })

I think a lot of the uses here could be accomplished using decorators if they were more generally applicable. If function decorators are ever implemented, then they would be able to cover this use case.

Many of the scenarios described in this thread should be handled by keyof operator introduced in #11929

Liero commented

keyof is absolutelly awesome! But still need something for name attributes, when databinding: <input name={nameof(this.state.firstName)} />

Yes, good to see some results in this area. But the use case for keyof is different. You still can't reference the name of:

  • Properties and members like @Liero mentioned
  • Type names, see comment
  • Parameters

@mhegazy

keyof is nice, but it's not enough. nameof would still be extremely useful when e.g. declaring the dependencies of computed properties in Aurelia:

@computedFrom(nameof this.foo.bar)
// should compile to: @computedFrom("this.foo.bar")

Or in error messages for invalid function arguments:

throw new Error(`The value of ${nameof options.foo} must be positive`)
// should compile to: throw new Error(`The value of ${"options.foo"} must be positive`)

This would finally make those things refactoring safe. They are currently a huge pain to maintain, and when you mess them up, it happens silently - you just get performance issues that can be extremely hard to debug, and misleading error messages.

I see some of the posts above talk about different variations of this and concerns around generics, etc. In my opinion, this should be as simple as possible - just give me exactly what I wrote as a string. So nameof Foo<Bar> should compile to exactly that - "Foo<Bar>". If the generics are not needed, those can just be removed later using a regex. I really don't see why this has to be so complicated.

Or if that is unacceptable, just strip away the damn generics, or don't support generic types - just please reconsider this. It is such a useful feature, and very much needed.

@thomas-darling if it were anything like the C# definition, nameof foo.bar would be "foo", not "foo.bar".

Anyway, you can achieve the type safe, computedFrom decorator for unqualified names, using

// computed-from.ts
export function computedFrom<K1 extends string, K2 extends string, K3 extends string>
  (prop1: K1, prop2: K2, prop3: K3): 
    (target: {[P in K1 | K2 | K3]}, key: string | number | symbol) => void;

export function computedFrom<K1 extends string, K2 extends string>
  (prop1: K1, prop2: K2): (target: {[P in K1 | K2]}, key: string | number | symbol) => void;

  export function computedFrom<K extends string>
    (prop: K): (target: {[P in K]}, key: string | number | symbol) => void;

and then consume it like this

// welcome.ts
import { computedFrom } from './computed-from';

export class Welcome {
  firstName: string;

  lastName: string;

  @computedFrom('firstName', 'lastName') get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }
}

qualified names like "foo.bar" are also possible.

jods4 commented

@aluanhaddad

qualified names like "foo.bar" are also possible.

This is something I somehow missed, care to elaborate?

I was thinking it would be possible to overload a tagged template string declaration but but upon further reflection I don't think it will work. I apologize for any confusion.

Right, so instead of introducing nameof you suggest to extend all libraries like immutablejs and etc?

@aluanhaddad also these examples you provided don't solve the "issue" that is having code references inside strings, which are pretty much unrefactorable automatically or easily. The main goal of nameof in my PoV is to make those string refactorable and searchable.

jods4 commented

@fredgalvao this might change #11997

@RyanCavanaugh If the new keyof feature really does solve this use case for nameof, can you provide an example...preferably using the TypeScript Playground to prove it works? Thanks!

Scratch that--I tried it myself and it works almost perfectly.

interface User {
    name: string;
    age: number;
}

var str1: keyof User;
var str2: keyof User;
var str3: keyof User;

str1 = "name"; // successful compile
console.log(str1);

str2 = "Age"; // compiler error due to typo
console.log(str2);

str3 = "age"; // successful compile
console.log(str3);

Playground source

When I try to use Rename Symbol (F2) in vs code, it does not rename my string which is what I would expect from a nameof(mySymbol) feature.

Additionally, I don't see a way to handle the use case with function parameters:

function log(name: string, age: number) {
    console.log(nameof(age), ' is ' , age);
}

So keyof gets us 80% of the way there I think but nameof is still better.

Update

I added aluanhaddad's example to cover both use cases here: Playground Source

Function parameter names are a harder beast to tame, because at runtime that name might be something else entirely, for it's one of the sure cases to be minified/uglyfied by 99% of post-processors.

My use-case is for sockets.

Using socket.io, socket emiters/receivers are linked using strings.

So currently I am writing this:
(Where showX() are interface implementations both client and server side)

public showLobby(): void {
    this.server.emit("showLobby");
 }

What I want to write:

public showLobby(): void {
    this.server.emit(nameof(this.showLobby));
 }

(I know I can write this.showLobby.name, but it would be nice to have compile time strings)

The second implementation would allow me to refactor my interfaces, and the client/server magic strings would also automatically change. As far as I can see this is not possible with keyof.

When I try to use Rename Symbol (F2) in vs code, it does not rename my string which is what I would expect from a nameof(mySymbol) feature.

Indeed but at least you get a compile time error letting you know that you must update these use sites. That makes the refactoring operation safe if not optimally convenient.

The same can be done for parameters so the log example works as well as the others and also demonstrates one of the really powerful aspects of this feature

function log<K extends keyof User>(name: K, value: User[K]) {
  console.log(name, ' is ', value);
}

const user: User = {
  name: 'Jill',
  age: 50
};

log(str1, user[str1]); // OK
log(str3, user[str3]); // OK
log(str3, user[str1]); // Error
log('age', user.name); // Error

deeply correlated f-bounds.

jods4 commented

@aluanhaddad I don't think your log example works exactly as you illustrate. My understanding is that the following would be perfectly OK:

function log<K extends keyof User>(name: K, value: User[K]) { }

const user: User = {
  firstName: 'John',
  lastName: 'Doe'
};

log('firstName', user.lastName);  // OK, unfortunately

This is because K is inferred to 'firstName' and hence, U[K] is inferred to string, which lastName satisfies.
Your example is nice only because each property had a different type.

jods4 commented

Indeed but at least you get a compile time error letting you know that you must update these use sites. That makes the refactoring operation safe if not optimally convenient.

Most of the time, but not always. You can make changes that result in conflicting names and still compile. Like renaming a method to introduce another method with the same name as the old one.

An even more important tool for me is Find all references, which simply doesn't work. Its substitue is a plain text Find, but (a) you need to get in the habit of never using Find references anymore, which is a shame and (b) for common names, Find is extremely noisy and turns up tons of false positive.

Go vote for #11997!

@RyanCavanaugh: how keyof can help with with immutable.js for instance? you cannot extend all immutable classes. nameof could help with that. As far as Angular2 goes with typescript and for performance it goes with immutable.js too, nameof is essential.

Liero commented

@RyanCavanaugh: maybe time to reopen? We've collected enough usecases where keyof does not help. Thanks

I hacked up a gulp task to do it for now. Just run it before the typescript compiler. If anyone is interested:

Probably not safe for production.

var replace = require('gulp-string-replace');

gulp.task("nameof", function () {
    var regexReplace = function (match) {
        // nameof(foo.bar) => foo.bar
        var propName = match.slice(7, -1).trim();
        // foo.bar => bar (also works if no . present)
        var endElement = propName.split(/[.]+/).pop();
        return '"' + endElement + '"';
    };

    return gulp.src("./src/**/*.ts")
        .pipe(replace(/nameof\s*\([^\)]*\)/, regexReplace))
        .pipe(gulp.dest("./nameof"));
});

@CoenraadS if you are using a gulp task to do this then you might be interested in ts-nameof as most of the work is already done there (I mentioned it earlier in this thread). I've been using nameof in typescript for quite some time now.

Cannot understand, if someone did ts-nameof, gulp task - why for ts team this is such a problem!

@vytautas-pranskunas- Based on the comments in this thread about keyof, I did this to enforce the correct usage of keys in Immutable:

export interface IAppState {
    isConnectedToServer: boolean
}

const checkKey = <T>(key: keyof T) => key;

export const reducer: Reducer<IAppState> = (state = initialState, action: KnownAction) => {
    switch (action.type){
        case 'CONNECTED_TO_SERVER':
            return state.set(checkKey<IAppState>('isConnectedToServer'), true)
        case 'DISCONNECTED_FROM_SERVER':
            return state.set(checkKey<IAppState>('isConnectedToServer12345'), false) //Compile time error :)

Arguably, it's more verbose than nameof would be, and you don't get any help when refactoring, but at least you get compile time checking.

@paulinfrancis Yes it can be done like that. But still main question stays unanswered: Why community should do all kind of helper libs and hacks to get such obvious behavior and why ts team refuse to implement this?

And then there's the refactoring support, which we also won't get with keyof. I understand and respect your reluctance to introduce new keywords, but this is a fundamental language feature that is sorely needed, and has clear and indisputable use cases - and the lack of it causes both serious pain and nasty bugs on a daily basis. What more does it take to get this reopened?

gleno commented

Every 3-4 months I come back to this thread only to be once again disappointed by the stubbornness to not include the nameof() operator. It's really a major hassle for a lot of people, and not including it in favor of some other use case is just... unbelievable.

Everyone seems to agree that it should work more or less exactly as C# does it.

I think we should organize a protest - drive up to Redmond and picket MS headquarters... as I'm seriously out of ideas.

not enough peope are giving a shit, creating new issues regarding known problem works the best