dsherret/ts-nameof

Recommend: Don't use this package

dsherret opened this issue ยท 16 comments

I now recommend not using this package or any other compiler transforms. It's neat, but it creates code that is not portable and makes it hard to switch to new build systems. The current solutions for injecting compiler transforms are hacky and I can't imagine the TS compiler ever supporting this out of the box.

I personally now just use the following function. It's simple and it works for most cases... of course it's not as featureful, but it gets you 90% of the way there.

export function nameof<TObject>(obj: TObject, key: keyof TObject): string;
export function nameof<TObject>(key: keyof TObject): string;
export function nameof(key1: any, key2?: any): any {
  return key2 ?? key1;
}

Example Use

import { nameof } from "./some-relative-import";

interface SomeInterface {
  someProperty: number;
}

// with types
console.log(nameof<SomeInterface>("someProperty")); // "someProperty"

// with values
const myVar: SomeInterface = { someProperty: 5 };
console.log(nameof(myVar, "someProperty")); // "someProperty"

Thank you for your candor. It would be awesome if you also write a note to this answer https://stackoverflow.com/a/33556815/1177597 when you have spare time.

Can you show how it should be used?

@PKnight-CheckWriters I updated the main post to show some examples.

I published package ts-keyof. Maybe it will be useful for someone. Thanks for ts-nameof! I used it about 3 years, but now I need use esbuild and swc.

@dsherret I'm on the fence about your recommendation... When one uses the keyof approach it's not automatically caught by VSCode refactoring tracking mechanism, and you get tons of compilation errors when renaming the keys.

If this issue was addressed by the VSCode team, I would probably try and make the switch.

@wh1t3cAt1k I published package ts-nameof-proxy, referenced properties can be renamed by VS Code and you can try to use it.

@wh1t3cAt1k If for you enough ts-keyof then you can disable useAliasesForRenames in VSCode: microsoft/TypeScript#29238 (comment)

Screen-Recording-2022-07-28-at-09 02 48

I declined rename for interface property in my lib, because it doesn't work with TypeScript Refactoring correctly: sotnikov-link/ts-keyof#1

@dsherret @sotnikov-link

If I am understanding it right, there is still one important consideration that still makes this library not deserving of deprecation.

When using ts-nameof, import type is often enough, which disappears completely at compile time, has no import side effects.

With runtime-based libraries, you need to actually import the whole dependency tree of the entity to "nameof", potentially introducing a build/runtime slowdown and even introducing circular dependencies.

I'm not completely persuaded that having no pre-processing steps is a big win. To me, it was never a hassle to set up a simple babel macro.

If there is a goal to transfer to swc, it might expose some extensibility too?

@dsherret Thanks for being transparent about the state of this package, it's much appreciated. Can you show me how I would go about implementing nameof.full, though? I see the full function defined in the exported namespace(s), but I don't see any function implementation in any typescript files... ๐Ÿค”

There's still no way to get the name of the interface itself as a string, though.

How can I use this simplified method for variables without a parent object?

Something like

export function get_some_info(
  date: string | number,
  ...etc
) {
  if (!date) {
    console.log(`${nameof(date)} is required!`);
    return;
  }

  // ...etc
}

@dsherret Thanks for being transparent about the state of this package, it's much appreciated. Can you show me how I would go about implementing nameof.full, though? I see the full function defined in the exported namespace(s), but I don't see any function implementation in any typescript files... thinking

I know this is quite a late answer but just for clarification:
You not finding a definition of nameof.full is because nameof.full doesn't run any code.

ts-nameof hooks into your compiler program (ttypescript, ts-patch or babel for example), finds all function calls to functions called nameof, nameof.full, nameof.split etc. and replaces them with the desired result.

So there is no drop-in replacement of ts-nameof. All we can do is wait and hope for typescript to implement it at some point.

How can I use this simplified method for variables without a parent object?

@celluj34 I think the package written by @sotnikov-link will help you.
You'd have to change your code to something like this:

import { keyof } from "ts-keyof";

export function get_some_info(
  date: string | number,
  ...etc
) {
  if (!date) {
    console.log(`${keyof({ date })} is required!`);
    return;
  }

  // ...etc

I now recommend not using this package or any other compiler transforms. It's neat, but it creates code that is not portable and makes it hard to switch to new build systems. The current solutions for injecting compiler transforms are hacky and I can't imagine the TS compiler ever supporting this out of the box.

I personally now just use the following function. It's simple and it works for most cases... of course it's not as featureful, but it gets you 90% of the way there.

export function nameof<TObject>(obj: TObject, key: keyof TObject): string;
export function nameof<TObject>(key: keyof TObject): string;
export function nameof(key1: any, key2?: any): any {
  return key2 ?? key1;
}

Example Use

import { nameof } from "./some-relative-import";

interface SomeInterface {
  someProperty: number;
}

// with types
console.log(nameof<SomeInterface>("someProperty")); // "someProperty"

// with values
const myVar: SomeInterface = { someProperty: 5 };
console.log(nameof(myVar, "someProperty")); // "someProperty"

Could we get some more explanation on why this is no longer recommended? It seems to me, if your environment is controllable, then the pros outway the cons.

manuth commented

Hello everyone
I know it's been a lot of time but I just wanted to note that only recently I indeed did publish a drop-in replacement for ts-nameof.

All you have to do is remove the @types/ts-nameof and ts-nameof packages and instead install my new "TypeScript nameof" project as described in the project's README.

Feel free to check it out:
https://github.com/typescript-nameof/nameof

improved simple solution

export function nameof<TObject>(obj: TObject, key: keyof TObject): string;
export function nameof<TObject>(key: keyof TObject): string;
export function nameof<TObject>(type: { new(...args: any[]): TObject }): string;
export function nameof(key1: any, key2?: any): any {
  if(typeof(key1) == 'function' && key1.name){
    return key1.name;
  }
  return key2 ?? key1;
}

add example

class User {}
nameof(User) โ†’ 'User'