Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nameof keyword in Typescript

As I have seen, there is no native nameof-keyword like C# has built into TypeScript . However, for the same reasons this exists in C#, I want to be able to refer to property names in a type safe manner.

This is especially useful in TypeScript when using jQuery plugins (Bootstrap-Tagsinput) or other libraries where the name of a property needs to be configured.

It could look like:

const name: string = nameof(Console.log); // 'name' is now equal to "log" 

The assignment of name should change too when Console.log got refactored and renamed.

What is the closest possible way of using such a feature in TypeScript as of now?

like image 346
Bruno Zell Avatar asked May 22 '18 14:05

Bruno Zell


People also ask

What is Nameof typescript?

ts-nameof is a library that provides the exact functionality as C# does. With this you can do: nameof(console); // => "console" nameof(console.

What is the use of Nameof in C#?

C# NameOf operator is used to get name of a variable, class or method. It returns a simple string as a result. In error prone code, it is useful to capture a method name, in which error occurred. We can use it for logging, validating parameters, checking events etc.


2 Answers

As you have already said, there is no built in functionality on TypeScript as of version 2.8. However, there are ways to get the same result:

Option 1: Using a library

ts-nameof is a library that provides the exact functionality as C# does. With this you can do:

nameof(console); // => "console" nameof(console.log); // => "log" nameof<MyInterface>(); // => "MyInterface" nameof<MyNamespace.MyInnerInterface>(); // => "MyInnerInterface" 

ts-simple-nameof offers an alternative. It basically parses a stringified lambda to figure out the property name:

nameof<Comment>(c => c.user); // => "user" nameof<Comment>(c => c.user.posts); // => "user.posts" 

Option 2: Define a helper function

You can easily define your own nameof that adds the type checking, however it will not refactor automatically as you'll still need to type a string literal:

const nameof = <T>(name: keyof T) => name; 

It will return the passed property name but will generate a compile time error when the property name does not exist on type T. Use it like so:

interface Person {     firstName: string;     lastName: string; }  const personName1 = nameof<Person>("firstName"); // => "firstName" const personName2 = nameof<Person>("noName");    // => compile time error 

Credits and more information about this

Update on helper function with TypeScript 2.9+

The type keyof T now not only resolves to a string, but to string | number | symbol (ref). If you still want to resolve strings only, use this implementation instead:

const nameof = <T>(name: Extract<keyof T, string>): string => name; 
like image 157
Bruno Zell Avatar answered Sep 26 '22 02:09

Bruno Zell


I think we often need more: to get class property names at runtime with compile-time validation. Renaming property will change nameOf expression. This is a really useful feature:

export type valueOf<T> = T[keyof T]; export function nameOf<T, V extends T[keyof T]>(f: (x: T) => V): valueOf<{ [K in keyof T]: T[K] extends V ? K : never }>; export function nameOf(f: (x: any) => any): keyof any {     var p = new Proxy({}, {         get: (target, key) => key     })     return f(p); } 

Usage example (no strings!):

if (update.key !== nameOf((_: SomeClass) => _.someProperty)) {    // ...                                } 

Example with existing instance:

export interface I_$<T> {     nameOf<V extends T[keyof T]>(f: (x: T) => V): valueOf<{ [K in keyof T]: T[K] extends V ? K : never }>; }  export function _$<T>(obj: T) {     return {         nameOf: (f: (x: any) => any) => {             return nameOf(f);         }     } as I_$<T>; } 

Usage:

let obj: SomeClass = ...; _$(obj).nameOf(x => x.someProperty); or _$<SomeClass>().nameOf(x => x.someProperty); 

resolved to 'someProperty'.

like image 23
SalientBrain Avatar answered Sep 23 '22 02:09

SalientBrain