最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Retrieve property name from lambda expression in TypeScript - Stack Overflow

programmeradmin4浏览0评论

I would like to strongly type property names

myMethod(model => model.userId);

public myMethod(model: () => any) {
    //Must print "userId"
}

I already know this won't work because JavaScript will evaluate userId.

It's easily doable in C#:

Get string property name from expression

Retrieving Property name from lambda expression

Is it possible to do it in TypeScript/JavaScript?

I would like to strongly type property names

myMethod(model => model.userId);

public myMethod(model: () => any) {
    //Must print "userId"
}

I already know this won't work because JavaScript will evaluate userId.

It's easily doable in C#:

Get string property name from expression

Retrieving Property name from lambda expression

Is it possible to do it in TypeScript/JavaScript?

Share Improve this question edited Jun 20, 2018 at 15:39 olivierr91 asked Jun 20, 2018 at 13:51 olivierr91olivierr91 1,4153 gold badges14 silver badges35 bronze badges 5
  • 2 Why not just pass the name in ? – Jonas Wilms Commented Jun 20, 2018 at 13:54
  • Instead of () => any, try () => User with interface User { userId: number }. Then when you do model() you will get a user. So you can directly do model().userId – Rajesh Commented Jun 20, 2018 at 13:58
  • @JonasW. Because it would not provide compile type checking of the property name. To put my question in context, this is for model validation and the method shows to the user which form field associated with the property of the model has an error. I already pass the name as a string, that's the thing I wanna get away from because it is too much error prone when renaming fields, etc. – olivierr91 Commented Jun 20, 2018 at 15:42
  • @Rajesh The method does not know in advance the name of the field, the caller must provide the name of the field. – olivierr91 Commented Jun 20, 2018 at 15:45
  • 1 @sixstorm1 you can do this in a typesafe way! Just have a look at my answer. – Jonas Wilms Commented Jun 20, 2018 at 15:56
Add a comment  | 

2 Answers 2

Reset to default 10

Unlike in C#, one can dynamically access properties by their name in JavaScript (and thus also Typescript), so you could just pass the name as a string to the function and use bracket notation to access the property:

myMethod(model, "userId")

Now the cool thing about typescript that this string can actually be typesafe:

function myMethod<T, K extends keyof T>(model: T, key: K) {
  const value = model[key];
  //...
 }

Read on


If you really want to do something similar like you did in C# (don't!!) Just do this:

function myMethod(model: () => any) {
   const key = model.toString().split(".")[1];
   //...
}

If I understand what you're asking, you'd like to inspect a property-retrieving arrow function, and return the name of the property it's returning? That sounds like a Bad Idea for a weakly/dynamically typed language like JavaScript, and something any sane person should run screaming from. Still, assuming I were either insane or being coerced, here's how I would go about trying to doing it in TypeScript 2.9 compiled to ES2015:

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

The function evilMagic takes a property-getting function and tries to return the name of the property it returns. The type of the output is hard to explain, but basically it will be some subset of keyof T where T is the argument the property-getting function expects. The implementation of the function uses a Proxy object p which acts like some object whose values are always the same as its keys. That is, p.foo is "foo", and p.bar is "bar", and p[10] is "10". Call the property-getter on p and, abracadabra, you have the name of the property.

Here's an example of its use:

interface Person {
  name: string;
  age: number;
  numberOfLimbs: number;      
}
const m = evilMagic((x: Person) => x.age); // typed as "age"|"numberOfLimbs";
console.log(m); // "age"

At compile time, TypeScript can only tell that m is one of "age" or "numberOfLimbs", because it only sees that the callback function returns a number. At runtime, you get "age" as you expect.

This whole thing scares me, though, and I feel unclean writing it; a function that expects a value of some type may do all kinds of crazy things when handed that proxy. I'd hate to think of code like that ending up in anything meant for production, although it could be a useful debugging tool. Please tell me you'd only use it for a debugging tool!

发布评论

评论列表(0)

  1. 暂无评论