【问题标题】:Typescript. How to get object's property name from its value?打字稿。如何从对象的值中获取对象的属性名称?
【发布时间】:2016-05-05 10:32:34
【问题描述】:

我正在编写一个用于immutable map 的打字稿类

class NavigableObject<T> {
    constructor(private obj: T, private path: string[] = []) { }

    To<R>(p: (x: T) => R): NavigableObject<R> {
        return new NavigableObject<R>(p(this.obj),
                       this.path.concat(this.getPropName(p(this.obj))));
    }

    getPath() {
        return this.path;
    }

    private getPropName(value) {
        for (var item in this.obj) {
            if (this.obj[item] === value) {
                return item;
            }
        }
    }
}

let test = {
    a: {
        a1: 1,
        a2: 1
    },
    b: {
        b1: 1,
        b2: 2
    }
}

let navigableTest = new NavigableObject(test);

navigableTest.To(m => m.b).To(m => m.b2).getPath(); // = ["b", "b2"]

navigableTest.To(m => m.a).To(m => m.a2).getPath(); // = ["a", "a1"] <-- I selected a2, tho

getPropName 方法有问题。当obj 有两个属性值相同时,只会匹配第一个属性。

有人知道如何解决这个问题吗?

【问题讨论】:

  • 你想要发生什么?一组键?非唯一性错误?什么?
  • 一个对象的键应该是唯一的。您能否举例说明如何/为什么要拥有一个具有多个同名键的对象?
  • 我的意思是,一个对象有两个属性值相同(键不同)。
  • @NiettheDarkAbsol 我想在选择属性时获得强类型,然后返回完整路径以用于immutable map

标签: javascript typescript immutable.js


【解决方案1】:

你可以使用this way of getting the property name:

class NavigableObject<T> {
    constructor(private obj: T, private path: string[] = []) { }

    To<R>(p: (x: T) => R): NavigableObject<R> {
        return new NavigableObject<R>(p(this.obj),
                       this.path.concat(this.getPropName(p)));
    }

    getPath() {
        return this.path;
    }

    private static propertyRegEx = /\.([^\.;]+);?\s*\}$/;

    private getPropName(propertyFunction: Function) {
        return NavigableObject.propertyRegEx.exec(propertyFunction.toString())[1];
    }
}

let test = {
    a: {
        a1: 1,
        a2: 1
    },
    b: {
        b1: 1,
        b2: 2
    }
}

let navigableTest = new NavigableObject(test);

navigableTest.To(m => m.b).To(m => m.b2).getPath(); // = ["b", "b2"]

navigableTest.To(m => m.a).To(m => m.a2).getPath(); // = ["a", "a2"]

【讨论】:

    【解决方案2】:

    未来就在这里。根据 Tim Perry 的 link,TypeScript 现在添加了 keyof,这是获取类的可用属性的绝佳功能。

    用法,根据 TypeScript documentation:

    interface Person {
        name: string;
        age: number;
        location: string;
    }
    
    type K1 = keyof Person; // "name" | "age" | "location"
    type K2 = keyof Person[];  // "length" | "push" | "pop" | "concat" | ...
    type K3 = keyof { [x: string]: Person };  // string
    

    【讨论】:

      【解决方案3】:

      您可以使用当前的查询方法在这里做任何简单的事情。您选择的属性是模棱两可的,因此没有简单的方法可以找到正确的路径。并不是说你的代码现在就“错误”了,只是有两个可能的正确答案,而且是任意选择的一个。

      您可以更改它选择哪些可能的键的规则,但没有明智的规则可以可靠地为您提供正确的单一答案。或者,您可以返回所有可能的答案,并有一个模棱两可的路径,但它似乎并不符合您的要求。

      可能有一种选择是做一些疯狂的事情,比如解析Esprima 提供的函数,甚至是正则表达式来计算出哪些属性被抓取,但这通常是个坏主意。除非你能保证To() 中提供的代码的确切形状,并且运行速度也很慢,否则这可能是复杂、脆弱、不可靠的。

      如果您确实希望能够选择这样的属性并确定使用的路径,则必须为您的 To 函数提供属性的键,而不仅仅是获取它所持有的值的函数.这是一个不太优雅的 API,但它是获得所需行为的唯一明智方式:

      class NavigableObject<T> {
          constructor(private obj: T, private path: string[] = []) { }
      
          To<R>(p: string): NavigableObject<R> {
              return new NavigableObject<R>(this.obj[p],
                             this.path.concat(p));
          }
      
          getPath() {
              return this.path;
          }
      }
      
      let test = {
          a: {
              a1: 1,
              a2: 1
          },
          b: {
              b1: 1,
              b2: 2
          }
      }
      
      let navigableTest = new NavigableObject(test);
      
      navigableTest.To('b').To('b2').getPath();
      
      navigableTest.To('a').To('a2').getPath();
      

      将来可能会在 TypeScript 中使用类型安全来执行此操作,但现在不行。 This PR 正在研究这些问题,但仍在讨论中,因此实施前还有一段时间。请注意,它仍然在上面的示例中提出基于字符串的方法,只是类型系统会检查字符串常量是否是所用类型的有效属性名称。

      【讨论】:

        【解决方案4】:

        不要在找到第一个值时终止 for,并返回具有匹配值的名称数组。我把如何处理多个名称的问题留给你。

        private getPropName (value) {
            var items = [];
            for (var item in this.obj) {
                if (this.obj[item] === value)
                    items.push (item);
            return items;
        }
        

        【讨论】:

          猜你喜欢
          • 2019-05-17
          • 2019-12-03
          • 2022-01-12
          • 2013-01-02
          • 2021-12-06
          • 2022-08-23
          • 1970-01-01
          • 1970-01-01
          • 2019-07-28
          相关资源
          最近更新 更多