Typescript. 如何从对象值获取对象属性名称?

3

我正在编写一个TypeScript类,用于与不可变映射一起使用。

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有两个具有相同值的属性时,只会匹配第一个属性。

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


1
你希望发生什么?键的数组?对于非唯一性的错误?还是其他什么? - Niet the Dark Absol
一个对象的键应该是唯一的。你能举个例子说明如何/为什么会有一个对象有多个相同名称的键吗? - Anthony E
我的意思是,一个对象有两个具有相同值的属性(不是相同的键)。 - Hoang Hiep
@NiettheDarkAbsol 我想在选择属性时获得强类型,然后返回完整路径以便与immutable map一起使用。 - Hoang Hiep
4个回答

3
未来已经到来。根据 Tim Perry 的 链接,TypeScript 现在已经添加了 keyof 功能,这是一个很好的获取类可用属性的特性。
使用方法可以参考 TypeScript 文档
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
您可以使用获取属性名称的方法:
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
您当前的查询方法无法轻松解决此问题。由于选择的属性不明确,因此很难找到正确的路径。目前的代码并非“错误”,只是有两个可能的正确答案,而它任意选择了其中一个。
您可以更改规则以选择可能的键中的哪一个,但没有可靠的合理规则可以保证得到正确的单一答案。或者,您可以返回所有可能的答案,并具有模糊的路径,但这似乎并不符合您的要求。
也许通过对提供的函数进行解析(使用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中的类型安全来实现这一点,但现在还不行。 这个PR正在研究这些问题,但它仍在讨论阶段,所以要等一段时间才能实现。请注意,它仍然提出了与上面示例相同的基于字符串的方法,只是类型系统将检查字符串常量是否为所使用的类型的有效属性名称。

0
不要在找到第一个匹配值时终止循环,并返回具有匹配值的名称数组。如何处理多个名称的问题由您自行解决。
private getPropName (value) {
    var items = [];
    for (var item in this.obj) {
        if (this.obj[item] === value)
            items.push (item);
    return items;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接