【问题标题】:Retrieve property name from lambda expression in TypeScript从 TypeScript 中的 lambda 表达式中检索属性名称
【发布时间】:2018-06-20 13:51:44
【问题描述】:

我想强输入属性名称

myMethod(model => model.userId);

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

我已经知道这行不通,因为 JavaScript 会评估 userId。

在 C# 中很容易实现:

Get string property name from expression

Retrieving Property name from lambda expression

是否可以在 TypeScript/JavaScript 中实现?

【问题讨论】:

  • 为什么不直接把名字传进去?
  • 代替() => any,试试() => Userinterface User { userId: number }。然后,当您执行model() 时,您将获得一个用户。所以你可以直接做model().userId
  • @JonasW。因为它不会提供属性名称的编译类型检查。把我的问题放在上下文中,这是为了模型验证,该方法向用户显示与模型属性关联的表单字段有错误。我已经将名称作为字符串传递,这就是我想要摆脱的事情,因为在重命名字段等时容易出错。
  • @Rajesh 方法事先不知道字段名,调用者必须提供字段名。
  • @sixstorm1 您可以以类型安全的方式执行此操作!看看我的回答吧。

标签: javascript typescript


【解决方案1】:

与 C# 不同,可以在 JavaScript(因此也包括 Typescript)中通过名称动态访问属性,因此您可以将名称作为字符串传递给函数并使用括号表示法来访问属性:

myMethod(model, "userId")

现在关于 typescript 很酷的一点是,这个字符串实际上可以是类型安全的:

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

Read on


如果你真的想像在 C# 中那样做一些类似的事情(不要!!)这样做:

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

【讨论】:

  • 差不多了,唯一的问题是模型需要被实例化。如果我的模型在运行时为空怎么办?我试过myMethod(typeof(MyModel), "userId"),但它不起作用。即使模型为空,我也需要能够检索属性名称。但到目前为止最好的答案。
  • 即使你不喜欢它,我更喜欢选项 2,它是调用者的更简洁的代码,并且不依赖于模型的实例:myMethod&lt;MyModel&gt;(m =&gt; m.userId)
  • 我正在尝试在 TypeScript 中使用 C# 的 Expr> 等效项。在 C#(例如 LINQ)中有很多这样的用法,还可以查看我发布的帖子的链接。我的具体用途是用于模型验证,该方法向用户显示与模型属性关联的表单字段有错误。目前我将模型属性的名称作为字符串传递,我想摆脱这种做法,因为它容易出错(例如当开发人员重命名字段时),经常忘记硬编码的字符串。
  • @sixtstorm1 错误!将属性作为字符串传递在 typescript 中容易出错。
  • 我目前的做法是没有类型检查,所以是的,它很容易出错。请仔细阅读我的评论。
【解决方案2】:

如果我理解你的问题,你想检查一个属性检索箭头函数,并返回它返回的属性的 name?对于像 JavaScript 这样的弱/动态类型语言来说,这听起来是个坏主意,任何理智的人都应该从中尖叫。尽管如此,假设我要么疯了要么被胁迫,这就是我将如何在编译为 ES2015 的 TypeScript 2.9 中尝试这样做:

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);
}

函数evilMagic 采用一个属性获取函数并尝试返回它返回的属性的名称。输出的 type 很难解释,但基本上它将是keyof T 的某个子集,其中T 是属性获取函数期望的参数。该函数的实现使用了一个Proxy 对象p,它的作用就像某个对象,其值始终与其键相同。也就是说,p.foo"foo"p.bar"bar"p[10]"10"。致电p 上的属性获取器,然后,abracadabra,您有属性的名称。

这是一个使用示例:

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

在编译时,TypeScript 只能判断m"age""numberOfLimbs" 之一,因为它只看到回调函数返回一个number。在运行时,您会按预期获得"age"

不过,这整件事让我害怕,我觉得写它不干净;一个期望某种类型的值的函数在交给该代理时可能会做各种疯狂的事情。我不希望这样的代码最终出现在任何用于生产的东西中,尽管它可能是一个有用的调试工具。请告诉我你只会将它用于调试工具! ?

无论如何,希望对您有所帮助。祝你好运!

【讨论】:

  • 不错的一个。但我会清理 tyeddefinitions,这太可怕了。
  • 我的问题是 TypeScript,它是一种强类型语言,因此我希望对类的属性进行强类型访问。对于我的要求,有很多可接受且有用的用法,请参阅上面我的帖子的链接、LINQ 等。不过,感谢您的回答。
  • 我认为这是最优雅的解决方案。当我清理应用程序中其他组件的数据时,我将它用于我的日期格式。我有一个装饰器,它扫描进来的对象,然后 lambda 选择必须清理哪些字段。感谢您的解决方案!
猜你喜欢
  • 2010-10-14
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-08
相关资源
最近更新 更多