打字稿。如何从对象的值中获取对象的 属性 名称?

Typescript. How to get object's property name from its value?

我正在写一个打字稿 class 用于 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有两个相同值的属性时,只会匹配第一个属性。

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

使用您当前的查询方法在这里做任何事情都不容易。 属性 你 select 是哪个是模棱两可的,所以没有简单的方法来获得正确的路径。这并不是说你的代码现在是 'wrong',只是有两个可能的正确答案,它是任意选择一个。

您可以更改有关它选择哪些可能键的规则,但没有任何合理的规则可以可靠地为您提供正确的单一答案。或者你可以 return 所有可能的答案,并且有一个模棱两可的路径,但它似乎并不像你正在寻找的那样。

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

如果您确实希望能够 select 这样的属性并知道用于特定的路径,则必须为 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 正在研究这些问题,但仍在进行一些讨论,因此需要一段时间才能实施。请注意,它仍然建议使用上面示例中的基于字符串的方法,只是类型系统将检查字符串常量是否有效 属性 所用类型的名称。

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

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

你可以使用 :

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"]

未来已来。根据 Tim Perry 的 link,TypeScript 现在添加了 keyof,这是获取 class.

可用属性的一个很好的特性

用法,根据 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