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

Created on 30 Dec 2014  ·  174Comments  ·  Source: microsoft/TypeScript

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));
Suggestion Waiting for TC39

Most helpful comment

Why after all this time, TS still has no mechanism to (e.g. nameof) to eliminate MAGIC STRINGS?

I don't understand why this thread has so much begging, justifications, and use cases. It's a no brainer - this should be a top priority. It's the most horrible part of JS which TS has not yet solved.

All 174 comments

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

:thumbsup:

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.

https://github.com/basarat/typescript-book/issues/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 :+1: :100: 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.

<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.

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.

@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> // <-- ???

@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.

@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

@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 :wink:. 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.

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 https://github.com/Microsoft/TypeScript/pull/11929

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.

@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.

@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.

@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.

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.

@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?

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

I've not checked the transforms API but I'd imagine it would be relatively easy to implement nameof in a transform. The transform may even be shipped with the compiler, but not an official part of it. From this point of view, it's been a really smart move from the team not to implement it prematurely.

@gcnew What easy? nameof([put valid identifier here]) -> last part of that identifier as string. I mean it's a glorified macro. In fact, at work we are just about to add a function nameof(thing:any) that gets replaced with exactly what @frodegil suggested above at post-compile with regex or whatever. I'm sure there are some edge cases that technically might require some more thought, but this is basically covering all use cases expressed in this thread. It would just make an infinity of sense to add nameof() it to a language plagued with magic strings and lack of reflection - instead we get keyof(). Palm. Face.

@gleno keyof is a very useful feature. The fact that the problems it solves are different from what people want from nameof is another story.

From my point of view nameof should not be implemented because it scavenges a valid identifier and adds non-standard expression level syntax. When Transforms API gets merged-in, it should be fairly straightforward to write a transfrom yourself or use the best community one.

@gcnew my understanding that transforms are from existing syntax -> existing syntax, nameof is new syntax so it's missing out, i hope i am wrong

@aleksey-bykov Yes, I think so as well. However if you have a function declaration such as:

declare function nameof(x: any): string;

and a transform that replaces all global nameof invocations with its argument stringified, it should work.

Edit: such a solution will not be sufficient where a string literal is expected. Nontheless it's a nice first step and the more compiler APIs become available the more it could be improved on.

@gcnew: If this is gonna be an official solution for nameof scenarios, then please write it to documentation, for example here: https://www.typescriptlang.org/docs/handbook/gulp.html

@gcnew If you look at Roslyn, the extension points are diagnostics, fixes and refactorings which provide help during development and have no risk of using any of them. The last thing I want to do is adding random packages which modify the very heart of the build pipeline, the compilation and supporting these non-existing language constructs with different hacks.
And then end up with problems like "we can't upgrade to TypeScript 2.x, because "nameof package" 0.1.2 does not support it yet, so 1) we will have to wait or 2) remove all usages of nameof from our code and loose it; or 3) reimplement it ourselves and maintain it forever" in a few months.

@Peter-Juhasz who makes you download it from npm? make it yourself and be your own 24/7 customer service

transformation api has very little to do with these circumstances

@Peter-Juhasz I feel for you and I do agree that nameof would be useful and add safety. However TypeScript is about modelling ECMAScript, not making up a new better language. Features such as keyof are purely type level, that's why they are getting implemented. And even for them there is resistance to allocate a keyword. In contrast, nameof is strictly an expression level construct. I don't think the team will ever be convinced to implement it, unless it's in the ECMAScript spec. I myself don't like the idea of scavenging the nameof identifier.

This leaves us with two options:

  • make an ECMAScript proposal and hope it gets traction
  • use the public Transfroms API and have a good approximation

The first option will take years (if it ever gets approved), the second should be decent enough. I have no experience with using the TS APIs, but I'd expect them to be stable and backward-compatible, thus the _nameof package_ should be stable as well.

I disagree Marin, nameof isn't a language feature, it only enables
type-checking (similarly to keyof), once compiled nameof disappears and its
argument is left as a string.
I don't see why this would need to be part of ecmascript if keyof doesn't.

On Feb 14, 2017 19:19, "Marin Marinov" notifications@github.com wrote:

@Peter-Juhasz https://github.com/Peter-Juhasz I feel for you and I do
agree that nameof would be useful and add safety. However TypeScript is
about modelling ECMAScript, not making up a new better language. Features
such as keyof are purely type level, that's why they are getting
implemented. And even for them there is resistance from the team to
allocate a keyword. In contrast, nameof is strictly an expression level
construct. I don't think the team will ever be convinced to implement it,
unless it's in the ECMAScript spec. I myself don't like the idea of
scavenging the nameof identifier.

This leaves us with two options:

  • make an ECMAScript proposal and hope it gets traction
  • use the public Transfroms API and have a good approximation

The first option will take years (if it ever gets approved), the second
should be decent enough. I have no experience with using the TS APIs, but
I'd expect them to be stable and backward-compatible, thus the nameof
package
should be stable as well.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/TypeScript/issues/1579#issuecomment-279790055,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABOZ9c-85P3x5AMakVDuxlNkH56me3POks5rcfAYgaJpZM4DNVgi
.

At least for interfaces, that go poof after compiling (no code produced and no impact on JS Standard)
we could have helper functions that expose all their metadata.
It's rather trivial (I'm currently doing it with a bash script), and
it would be really useful to have not only nameOf, but implements/extends informaton, the list of fields, fields names and their arity (signature)as well.

Honk me if interested, we could write a proposal and in the worst case a pre-processor.
(time and motivation permitting)

--R

@gcnew:
1. nameof will never be in ES spec. I'm pretty confident to say so, because nameof doesn't make much sense in EcmaScript. Here's why: Because of dynamic typing in javascript, nameof(myObject.MyProperty) is exactly the same as "myProperty". Both are basically "magic strings". There is no reason to use the verbose nameof syntax in javascript. Or do you see any advandage of using nameof in javascript?

As @RyanCavanaugh mentioned, only standardized ES features or features that will never be in ES spec can be considered for typescript in order to avoid confusion like it happend with modules. This is the second case.

  1. The very basic purpose of TypeScript is to provide static typing and allow for better refactoring tools. Property names are required very often in DataBinding scenarios and doing so in type safe manner is exactly what you would expect from TypeScript.

I'm a pragmatic programmer and I don't care how it can be done if it can be done. Choose whatever you think's best for the product considering ES compatibility, easy-of-use, performance or whatever you think is relevant. If you decide to go with Transfroms API, let it be so, but for god's sake, this issue deserves a solution.

@Liero You can never know whether a valid identifier would be used in future versions. It might not be used by ECMA directly or not for the purpose discussed in-here, but it might find its use in browser APIs. Consider a global function nameof(node: Node): string | undefined which reads the name attribute of a DOM node. Will such a function ever be standardised? Probably not _this one_ but maybe one concerning certificates or Symbols. The point is nameof cannot be borrowed as a keyword. There is code already using it for a local variable names and future APIs may use it as well. It would be an unfortunate breaking change, whilst diverging from the ECMAScript specification.

The solution is either to lift nameof to the type level, or implement nameof as an optional pre/post processing step, that is not part of the official language.

A type-level, fully erasable nameof might be:

declare const options: {
    easing?: string,
    duration: number
};

if (!options.easing) {
    throw new Error(<nameof options.easing> 'easing');
}

Level

nameof(path.to.identifier) will never survive the compilation process.Seeing nameof in the final compiled code is definitely and exactly NOT what everyone asking for the feature wants. We want it to disappear, and leave behind the string "identifier", which makes the implementation of nameof as a compile/type level precisely what it needs to be. The only difference between:

type AliasToUnion = A | B;
and
nameof(object.property)

is the outcome: one gives an empty string and the other gives a non-empty string. Both are type level structures, and both have their shell disappear once the compilation is done.

Collision

If now the argument is that we can't risk a keyword collision between typescript's nameof and future ecma's nameof, then I'd say the same argument applies to pretty much everything typescript has that is not in the intersection with es8 drafts, which is basically a lot of type stuff, thus making this argument quite bogus.

Desire

Even if we don't go the keyword way, this pretty much sums for me why I still think it's optimally typescript's job to do this (be it a transformation, a compile level function, a type level structure, an ecma proposal, etc):

The very basic purpose of TypeScript is to provide static typing and allow for better refactoring tools. Property names are required very often in DataBinding scenarios and doing so in type safe manner is exactly what you would expect from TypeScript.

Suggestion

<nameof options.easing> 'easing'

Seems like it almost does it, but we'd have to mannually refactor that not-so-magical string anyway, with the only difference that we'd maybe have the compiler tell us that it's value does not represent the actual name of the identifier. It's a refactor->compile->see_error->go_back_and_properly_refactor flow instead of a refactor_properly one. With that, I'd rather insist in a more complete version instead of this proposal. Now, @gcnew, if we could make <> a structure that didn't need a value to be cast, that'd be awesome (I don't think we can atm).

guys, nameof is sure needed, in mean while there is 90% capable workaround which only takes a bit of extra typing:

type NamesOf<T> = { [P in keyof T]: P }
interface Data {
    name: string;
    value: number;
}
const propertyNames: NamesOf<Data> = {
    name: 'name,' // <-- not a magic string anymore, CAN ONLY BE `name`
    value: 'value' // <-- not a magic string anymore, CAN ONLY BE `value`
}

image

@gcnew:

Consider a global function nameof(node: Node): string | undefined

usage in existing javascript files - no problem at all. In typescript conflicts can be avoided by:

var _nameof = window.nameof; //problem solved

BTW, in javascript the function would be probably called nameOf

@fredgalvao: your suggestion: <nameof options.easing> already conflicts with JSX, resp TSX

@Liero that was not my suggestion, I was just commenting on @gcnew 's suggestion.

Ok, what if there was typeside nameof operator?

Something that would look like this:

const obj = {x:1};
function abc(thing:nameof(obj.x)){ }

Which would behave exactly as

function abc(thing:"x"){ }

It's not as clean as nameof(accessor)->string, but I think it could be quite useful.

I consider nameOfas a compile-time keyword only, and can't see any runtime or global use of it (never say never, but its unlikely, IMO - call it TSNameOf, and it's even more unlikely)

Should we be afraid of collisions? If, or when, EcmaScript becomes a language that need the nameOf keyword in the future, I assume it has evolved so far, and adopted all features of TS so that we won't need TS any longer.

nameOf in TS should behave the exact same way as nameOfin C#

C# (v5) specs:

The nameof expression is a constant. In all cases, nameof(...) is evaluated at compile-time to produce a string. Its argument is not evaluated at runtime, and is considered unreachable code (however it does not emit an "unreachable code" warning).

I've spent too much time "refactoring" string-literals in my TS/Angular code by now. After all, the main purpose of Typescript is to be a strongly-typed alternative, right?

Hurry, include the compile-time nameOf keyword before TS becomes obsolete :)

What about a custom nameOf function like the following?

declare const options: {
    easing: string,
    duration: number,
};

function nameOf<T, K extends keyof T>(_: T, key: K) {
    return key;
}

nameOf(options, 'easing'); // 'easing'
nameOf({ options }, 'options'); // options

PS: keyof refactoring is tracked by https://github.com/Microsoft/TypeScript/issues/11997.

I have a very simple implementation of nameof implemented with the transformation api (#13940) that I did quickly this morning (See this branch of ts-nameof—specifically this file and used in this file).

If we had a way in tsconfig.json to specify paths to external before & after transformations while compiling I feel that this would be more powerful than having implementations directly in the compiler.

Edit: The library now supports babel and the typescript compiler.

For my two cents, I'd love to see nameof purely to get rid of strings in bindings with Inversify.

I was just looking into this as well. I was hoping this would be an obvious feature implemented by now. I was hoping to get event string names from method names in a custom event setup (bla bla bla ;)), among other things (such as storing namespace and type metadata, etc.). Hopefully, we won't need to discuss this for another 2 years.

I would love to see that, too. I'm working with some ASP.NET AJAX legacy code and have to deal with stuff like:

DerivedClass.registerClass( "DerivedClass", BaseClass );

It would be really nice to do this instead:

DerivedClass.registerClass( nameof( DerivedClass ), BaseClass );

Currently, renaming DerivedClass will break the Code.

nameof() would be nice for using Immutable.js Record.

This code

class MyRecord extends Record({value: ""}) {
  readonly value: string;

  withValue(newValue: string) {
    return this.set("value", newValue) as this;
  }
}

can be rewritten like

class MyRecord extends Record({value: ""}) {
  readonly value: string;

  withValue(newValue: string) {
    return this.set(nameof(this.value), newValue) as this;
  }
}

, which is safe for renaming.

Potential workaround for name of class or variable:

function nameof<T, P extends keyof T>(descriptor: {[P in keyof T]?: T[P]; }): P
{
    for(var key in descriptor) {
        if(descriptor.hasOwnProperty(key)) {
            return key as P;
        }
    }
}

class Test { }
var test= ""

var x = nameof({ Test })  // x: "Test" = "Test"
var y = nameof({ test })  // y: "test" = "test"

@jankaspar Is it possible to have Test.foo with your example where Test - class, foo - class property?

@vytautas-pranskunas-
No, for that you are much better using the mapped types. Something like this:

class Test {
    foo: string
}

function propertyName<T>(name: keyof T){ 
    return name;
}
propertyName<Test>("foo") 
propertyName<Test>("bar")  // error

note that it's not perfect. I have attempted using things like that and it failed, because the interfaces were too complex, and the compiler, instead of generating "field1" | "field2" for the keyof T, generated something like any, and anything would compile.

Thank you!
Spark by Readdle

Is there any consensus on how to do this with classes and interfaces? I see how I can just keyof but basically I want to use nameof(type) so it would make logging easier e.g., LogManager.getLogger(nameof(MyClass))

@niemyjski This is available with standard JavaScript.

class MyClass {
   hello(n) { return 'hello ' + n; }
}
console.log(MyClass.name);

See Function.name

@styfle That fails if the code is minified, which is why this is asking for a compile-time operator that produces a string in the output and won't be affected by minification

@dpogue most minifiers give an option to disable name mangling (globally, or for annotated functions/classes); that might satisfy?

It may also be surprising to some that nameof(MyClass) != MyClass.name (in the minified case)

Also, #8 and #16037 might have some related discussions to keep an eye on

Yes, we need something reliable...

Thanks
-Blake Niemyjski

On Wed, Jul 19, 2017 at 11:47 AM, Ian MacLeod notifications@github.com
wrote:

@dpogue https://github.com/dpogue most minifiers give an option to
disable name mangling (globally, or for annotated functions/classes); that
might satisfy?

Also, #8 https://github.com/Microsoft/TypeScript/issues/8 and #16037
https://github.com/Microsoft/TypeScript/issues/16037 might have some
related discussions to keep an eye on


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/TypeScript/issues/1579#issuecomment-316447179,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AA-So1aBKzLHIsS4-wYSeZmtNOf55B9Rks5sPjMYgaJpZM4DNVgi
.

@nevir and @dpogue this solution may not work where you're attempting to resolve by interface and not a concrete type via DI - i.e. does not solve for interface name availability at runtime.

I have found a need for something like this recently sincekeyof does not allow qualified identifiers.

Since the API I used in my example now supports qualified names using dotted path strings, the augmentation I was using to improve its type safety:

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;

can no longer express its capabilities.

Why after all this time, TS still has no mechanism to (e.g. nameof) to eliminate MAGIC STRINGS?

I don't understand why this thread has so much begging, justifications, and use cases. It's a no brainer - this should be a top priority. It's the most horrible part of JS which TS has not yet solved.

+1

this would be a useful feature

So what's the story people, is this happening?

As @jakkaj mentioned earlier, this would be a really great feature to use with inversify.

Here's an example of what you _currently_ have to do when you use interfaces:

interface Weapon {
    hit(): string;
}

// Define an object that contains all of the type 
// symbols so that we only use magic strings once.
let TYPES = {
    Weapon: Symbol("Weapon")  // Magic String! Oh No!
};

@injectable()
class Ninja {
    constructor(@inject(TYPES.Weapon) weapon: Weapon) { }
}

With a nameof operator, you could at least get rid of the magic string, but you could also get rid of the object that holds all the type symbols:

interface Weapon {
    hit(): string;
}

@injectable()
class Ninja {
    constructor(@inject(Symbol(nameof(Weapon))) weapon: Weapon) { }
}

I believe that strings can also be used to declare what is injected, so you could even change the constructor to this:

@injectable()
class Ninja {
    constructor(@inject(nameof(Weapon)) weapon: Weapon) { }
}

@reduckted

Here's an example of what you currently have to do when you use interfaces:

interface Weapon {
    hit(): string;
}

// Define an object that contains all of the type 
// symbols so that we only use magic strings once.
let TYPES = {
    Weapon: Symbol("Weapon")  // Magic String! Oh No!
};

@injectable()
class Ninja {
    constructor(@inject(TYPES.Weapon) weapon: Weapon) { }
}

Actually, that is not a magic string. You could just as well write

const types = { // this should be frozen in real code.
  Weapon: Symbol()
};

and the implications would be the same (all consuming code must reference the Weapon property of the types object).

With a nameof operator, you could at least get rid of the magic string, but you could also get rid of the object that holds all the type symbols:

interface Weapon {
    hit(): string;
}

@injectable()
class Ninja {
    constructor(@inject(Symbol(nameof(Weapon))) weapon: Weapon) { }
}

This would likely not have the desired effect as each time Symbol is called it creates a new unique value

Symbol("x") === Symbol("x") // false

I believe that strings can also be used to declare what is injected, so you could even change the constructor to this:

@injectable()
class Ninja {
    constructor(@inject(nameof(Weapon)) weapon: Weapon) { }
}

If the proposed functionality is analogous to C#'s nameof operator, this would result in @inject("Weapon"), introducing hidden global dependencies and token collisions.

Something like

interface Weapon {
    hit(): string;
}
const Weapon = Symbol();
export default Weapon;

seems preferable but it is still not ideal since. The trouble is keeping the names in sync to sustain declaration merging, but is not related to the actual values.

There are plenty of other reasons to want nameof, but I don't think it is a good fit for DI.

@aluanhaddad

This would likely not have the desired effect as each time Symbol is called it creates a new unique value

Ah yes, that's true. Symbol.for would fix that, but as you point out, the name you use for the symbol is somewhat irrelevant if you only create the symbol once and export it.

this would result in @inject("Weapon"), introducing hidden global dependencies and token collisions.

Yes, there is the potential for token collisions, but on a small project where all interfaces have a unique name, it would work perfectly fine (and InversifyJS makes it really easy to manage, as I've recently discovered 😁).

I can see nameof being a good fit for dependency injection _if you're aware of the limitations_. Just like, as someone mentioned earlier in this thread, there would be limitations when using nameof with minified code. For example, you might use nameof to get the name of a parameter. During minification, the parameter name is changed, but the compiler-generated "magic string" keeps the original name. Maybe you want the original name, or maybe you want the modified name. As long as you're aware of the limitations that would come with nameof, you'll be fine.

+1

Here's a couple more hacky workaround functions that work for my purposes so far, which can get the name or path of a nested prop with static checking.

function nameOf<T>(obj: T) {
  let name: string | undefined;

  const makeCopy = (obj: any): any => {
    const copy = {};
    for (const key in obj) {
      Object.defineProperty(copy, key, {
        get() {
          name = key;
          const value = obj[key];
          if (value && typeof value === "object") {
            return makeCopy(value);
          }
          return value;
        }
      });
    }
    return copy;
  };

  return (accessor: { (x: T): any }): string | undefined => {
    name = undefined;
    accessor(makeCopy(obj));
    return name;
  };
}

function pathOf<T>(obj: T) {
  let path: string[] = [];

  const makeCopy = (obj: any): any => {
    const copy = {};
    for (const key in obj) {
      Object.defineProperty(copy, key, {
        get() {
          path.push(key);
          const value = obj[key];
          if (value && typeof value === "object") {
            return makeCopy(value);
          }
          return value;
        }
      });
    }
    return copy;
  };

  return (accessor: { (x: T): any }): string[] => {
    path = [];
    accessor(makeCopy(obj));
    return path;
  };
}

Example usage:
screen shot 2018-02-26 at 15 59 00

Why this is expression-level operator when it should be on type-level?

Like this:

type Meme = {}
type S = nameof Meme // NameOf<Meme> perhaps better?

const wrong: S = "wrong" // Type '"wrong"' is not assignable to type '"Meme"'.
const ok: S = "Meme"

@goodmind Why on earth would you want to assign the name of “Meme” (theoretically a string) to a type? That makes no sense. Did you mean “typeof”?

const Why_does_TypeScript_not_have_this_yet: Explanation = { provided: false, reason: null };
console.log(`${nameof(Why_does_TypeScript_not_have_this_yet)}?`);
console.log(Why_does_TypeScript_not_have_this_yet);

if (!Why_does_TypeScript_not_have_this_yet.provided && (new Date()).getFullYear() >= 2018)
    this.me.sad = true;

I highly doubt they would ever implement this because nameof is not in the type namespace and not in any ECMAScript proposals.

That's not a problem though... if #14419 ("Plugin Support for Custom Transformers") were implemented then we could use our own implementations of nameof, which could be more powerful than a standard nameof function.

For example, if #14419 were implemented, then we would be able to specify the transformation plugin (example nameof implementation here) in tsconfig.json:

{
  "compilerOptions": {
    "customTransformers": {
      "before": ["node_modules/ts-nameof"]
    }
  }
}

So overall, I think it makes more sense to ask for #14419 than this issue because it opens the door to nameof and other transformations being easily integrated into a project.

Honestly it would be better if this was straight up closed with "We don't want this, sorry not sorry" then the silence and lack of triage.

ಠ_ಠ

Here is a runtime version that works well for my needs:

/** Returns the name of a namespace or variable reference at runtime. */
function nameof(selector: () => any, fullname = false) {
    var s = '' + selector;
    var m = s.match(/return\s+([A-Z$_.]+)/i)
        || s.match(/.*?(?:=>|function.*?{)\s*([A-Z$_.]+)/i);
    var name = m && m[1] || "";
    return fullname ? name : name.split('.').reverse()[0];
}

If namespace is A.B.C and inside 'C' scope:
Usage: nameof(()=>A.B.C) // (returns "C")
Usage: nameof(()=>A.B.C, true) // (returns "A.B.C")

Works great when refactoring as well. I only use it to pull namespace+class paths when the script first loads, so it's more than efficient for the task.

+1
Adding that feature will be very helpful.
Use case for example:

ngOnChanges(changes: SimpleChanges) {
    let itemPropName: keyof MapComponent<T> = "items";
    if (changes[itemPropName])
      this.setItems();

    let centerCoordsPropName: keyof MapComponent<T> = "centerCoords";
    if (changes[centerCoordsPropName])
      this.goToCoordinates();
  }

@injectable()
class Ninja {
constructor(@inject(nameof(Weapon)) weapon: Weapon) { }
}

Exactly what I want

@rayncc no need to use nameof here if you use a framework that relies on decorators, such as TypeScript IOC, Angular 2+, or @fluffy-spoon/inverse.

@ffMathy

I disagree, nameof would be especially useful for injecting interfaces, which only exist in name, but not as an actual JS object. Which is why you can't do @Inject(MyInterface), but instead you have to do @Inject('MyInterface'). If nameof was atleast available for interfaces, we could do @Inject(nameof MyInterface), which would be much cleaner than a string (which is prone for typos), and more importantly, would work well with refactors.

С# has nameof why not add alike feature in TypeScript? It will be very helpful. If will improve code robustness. I.e. instead of if (protoPropertyIsSet(msg, "expiration_utc_time")) we could write
if (protoPropertyIsSet(msg, nameof(msg.expiration_utc_time))).

@stasberkov C# is in full control of their syntax, Typescript has to tip-toe around JS and any future JS developments. Except for enums, type annotations and type assertions are the major extensions to expression level syntax. Any new expression level syntax has the danger of diverging from JS and making TS incompatible at some level with JS. This is why adding nameof is not an easy decision. Implementing it is trivial, but the long term implications might end up biting back.

@stasberkov C# is in full control of their syntax, Typescript has to tip-toe around JS and any future JS developments. Except for enums, type annotations and type assertions are the major extensions to expression level syntax. Any new expression level syntax has the danger of diverging from JS and making TS incompatible at some level with JS. This is why adding nameof is not an easy decision. Implementing it is trivial, but the long term implications might end up biting back.

The expression nameof(XXX) will be compiled to a constant string, which I think is totally compatible with JS

@rayncc What he meant is: what if JS later adds in a nameof operator which behaves differently than TypeScript's nameof operator?

Then there would be a conflict, which has to be resolved in some way (and there aren't any easy answers for that).

On the other hand, nameof isn't a reserved word in JS, so the chances of TC39 adding it as an operator is quite low. So I think it's pretty safe for TypeScript to add it.

I don't think that this case is different from generics or typings that can be implemented in JavaScript in a differrent way in the future... TypeScript then should support it while also supporting the TS way of implementing those concepts.

The are so many use cases for this, for example NativeScript's notifyPropertyChange( nameof( property) ) method. BTW, they have done this before with the decorator stuff. The solution there was to simply hide it behind an 'experimental' compiler flag.

@markusmauch lol, look how this ended up for them

decorators is not good example to follow

On the other hand, nameof isn't a reserved word in JS, so the chances of TC39 adding it as an operator is quite low.

Unary prefix operators don't need to be reserved words because op expr is already an invalid expression for any op that isn't an operator. For example, await was not previously a reserved word, but there wasn't any problem adding it to the language because it only ever appears in an unambiguous unary operator position.

Might I also point out ew do have keyof which is actually much more powerful than nameof, the result of nameof is just a string, keyof lets us constrain input much better.

While not as pretty as the nameof operator, we can use keyof to do something similar enough using a function IMO:

function nameof<T>(k: keyof T) : keyof T {
    return k
}
class Person {
    static defaultName: string;
    name!: string;
    studies!: {
        highSchool: string
        unversity: string
    }
}
let foo : { bar: { baz : { x: number }}}
let k1 = nameof<Person>("name")
let k2 = nameof<Person['studies']>("unversity")
let k3 = nameof<typeof Person>("defaultName")
let k4 = nameof<typeof foo['bar']['baz']>("x")

For the simple case it looks decent, for nested paths, it does look a bit wonky, and you can't use it on privates.

@dragomirtitian even though that acomplishes the {access members by name in a type safe approach} issue (which is awesome), that won't make the compiler automatically help developers on refactorings, nor would it help the IDE look for references of a member without doing text search.

@goodmind I really don't see your point. I agree that decorators turned out to be quite different than originally anticipated but that's what 'experimental' means. It means 'use it but be aware that it might change'. Anyway, I'm glad I can use them now and when they become part of the JavaScript language I will either change my code accordingly or keep the compiler switch in place. The same could be done with nameof. Decorator support was added to win Googe over. But obviously the needs of the community are not equally important.

@dragomirtitian it's been talked about in the collapsed posts within this issue.

To summarize, keyof helps in the scenarios you mentioned, but still doesn't get some common use cases where class or interface/type identifier names would be nice to keep synced up.

Ex. when using dependency injection frameworks (@inject(nameof(Something))), throwing nice argument error messages...

function add(a: number, b: number) {
    throwArgumentErrorIfNaN(a, nameof(a));
    throwArgumentErrorIfNaN(b, nameof(b));

    return a + b;
}

..., in test description names...

import { CustomCollection } from "some-library";

describe(nameof(CustomCollection), () => {
    describe(nameof<CustomCollection>(c => c.add), () => {
    });
});

..., when writing log statements (logger.log(nameof(ClassName), LogLevel.Info, "Some log message.")), and probably more.

There definitely is some benefit to adding nameof, but in my opinion it shouldn't be done by TypeScript as this suggestion doesn't meet this guideline:

  • This could be implemented without emitting different JS based on the types of the expressions.

Edit: My bad, was thinking of this non-goal:

> * Add or rely on run-time type information in programs, or emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.

Also, I don't see what motivation TC39 would have for adding this to JS as JS developers wouldn't benefit from this because writing nameof(Identifier) is only a little bit better than writing "Identifier" in JS due to no compile time errors when using an incorrectly named identifier (I guess there could be runtime ones, but that's not as nice). Also, TC39 wouldn't include support for nameof for identifiers in the type domain (such as interface and type alias names).

Its place is probably for this to be a compiler plugin (shameless plug) that people can choose to include in their projects.

With regarding whether this type of feature is the a good fit for TypeScirpt based on the guideline mentioned by @dsherret I wonder if there is a more library centric way that could be used by linters to pick up stringly typed code that encodes the intention more clearly to access the name of a variable or property. I tried playing around quickly with a concept using ES6 tagged template literals but unfortunately it does appear that one can access the original template string including the template parameter syntax which would have been a nice way to both provide runtime checking as well as allowing simple one liner cases for variables. The basic idea works reasonably for simple one level deep properties though and the syntax is arguably a bit better than a simple function call with parameters passed in.

const props = {
  name : "Little John",
  slogan: "Rolls are rolls and tools are tools."
}

console.log(nameof`${props}.slogan`) // slogan
console.log(nameof`${props}.foo`) // ERROR: Property 'foo' not found on object 

Ultimately what I think we really want is a somewhat more common way to express the name symbol name resolution that could be caught easier by some compiler/transpiler, linter, or at least runtime in that preferred order to reduce bugs and to provide for more resilient code needs to have these names available in some form at run-time.

There definitely is some benefit to adding nameof, but in my opinion it shouldn't be done by TypeScript as this suggestion doesn't meet this guideline:

  • This could be implemented without emitting different JS based on the types of the expressions.

Could you clarify as to what different JS means in this context? For example, if nameof<InterfaceA> and nameof<InterfaceB> both output a string that have different values but share the same type, is that considered different?

@JLWalsh I think I might have misread that as not emitting different JS based on the kinds of expressions (I was half going from memory when I copied and pasted it). So taking a call expression and emitting a string literal. I'm not sure now exactly what that point means now, but my brain is fried after programming for way too long today.

I was thinking of this non-goal:

  • Add or rely on run-time type information in programs, or emit different code based on the results of the type system. Instead, encourage programming patterns that do not require run-time metadata.

👍 Please add this

@rjamesnw
In case of numbers in expression, I did some modification:

function nameof(selector: () => any, fullname = false) {
    var s = '' + selector;
    var m = s.match(/return\s+([A-Z0-9$_.]+)/i)
        || s.match(/.*?(?:=>|function.*?{)\s*([A-Z0-9$_.]+)/i);
    var name = m && m[1] || "";
    return fullname ? name : name.split('.').reverse()[0];
}

Locking Waiting for TC39 threads as policy since there's not really anything to talk about except complaining that TC39 hasn't done it yet 🙃

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fwanicka picture fwanicka  ·  3Comments

dlaberge picture dlaberge  ·  3Comments

siddjain picture siddjain  ·  3Comments

DanielRosenwasser picture DanielRosenwasser  ·  3Comments

seanzer picture seanzer  ·  3Comments