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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37048274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:28:01  来源:igfitidea点击:

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

javascripttypescriptimmutable.js

提问by Hoang Hiep

I'm writing a typescript class for using with 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

There is a problem with getPropNamemethod. When objhas two properties with same value, only the first property will be matched.

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

Does anyone know how to work around this?

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

采纳答案by David Sherret

You could use 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"]

回答by Hyman Koppa

The future is here. As per Tim Perry's link, TypeScript has now added keyof, which is a great feature for getting the available properties of a class.

未来就在这里。根据 Tim Perry 的链接,TypeScript 现在添加了keyof,这是获取类的可用属性的一个很好的功能。

Usage, as per the TypeScript documentation:

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

回答by Tim Perry

There's nothing easy you can do here with your current querying approach. It's ambiguous which property you're selecting, so there's going to be no easy way to get the right path. It's not that your code is 'wrong' now as such, it's just that there's two possible correct answers, and it's picked one arbitrarily.

使用当前的查询方法,您无法在此轻松完成。您正在选择哪个属性是不明确的,因此没有简单的方法可以找到正确的路径。并不是说你的代码现在是“错误的”,只是有两个可能的正确答案,并且随意选择了一个。

You can change the rules on which of the possible keys it picks, but there are no sensible rules that will reliably get you the right single answer. Alternatively you could return all the possible answers, and have an ambiguous path, but it doesn't seem like that does what you're looking for.

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

There might be one option by doing crazy things like parsing the function provided with Esprimaor even regular expressions to work out which properties are being grabbed, but it's generally going to be a bad idea. That's likely to be complex and fragile, unreliable unless you can guarantee the exact shape of the code you'll be provided in To(), and run pretty slowly too.

可能有一种选择,比如解析Esprima提供的函数或什至正则表达式来确定哪些属性被抓取,但这通常不是一个好主意。这可能是复杂和脆弱的,不可靠的,除非您可以保证将在 中提供的代码的确切形状To(),并且运行速度也很慢。

If you do want to be able to select properties like this and know the path used for certain, you'll have to give your Tofunction the key of the property, not just a function to get the value it holds. It's a less elegant API, but it's the only sensible way you're going to get the behaviour you're looking for:

如果您确实希望能够像这样选择属性并知道特定使用的路径,则必须为您的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();

In future it might be possible to do this with type safety in TypeScript, but not right now. This PRis looking into exactly these issues, but it's still under some discussion yet, so there'll be a while until it's implemented. Note that it's still proposing the string-based approach from the example above, it's just the type system will check that string constants are valid property names for the types used.

将来可能可以在 TypeScript 中通过类型安全来做到这一点,但现在不行。这个 PR正在研究这些问题,但它仍在讨论中,所以需要一段时间才能实施。请注意,它仍然提出了上面示例中的基于字符串的方法,只是类型系统将检查字符串常量是否是所用类型的有效属性名称。

回答by HBP

Don't terminate the for on the first value found, and return an array of names with matching values. I leave to you the propblem of how to handle multiple names.

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

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