typescript 打字稿接口属性到字符串

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29600539/
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 02:59:07  来源:igfitidea点击:

Typescript interface property to string

javascripttypescript

提问by Antoine ESTEVE

Question/Answer - Update 2019

问题/答案 - 2019 年更新

This questions was asked 5 years ago, and I had very little understanding of Typescript! I don't want to remove it because there is still some people reading this post.

这个问题是5年前问的,我对Typescript了解很少!我不想删除它,因为还有人在阅读这篇文章。

If you have a function that takes an object and a property of this object as parameter, you have a lot of options:

如果您有一个将对象和该对象的属性作为参数的函数,那么您有很多选择:

const foo = <T extends object>(object: T, property: keyof T) => {
    // You can use object[property] here
};
// OR
const bar = <TKey extends string>(
    object: Record<TKey, unknown>,
    property: TKey,
) => {
    // You can use object[property] here
};
// ...

Don't use this question answers please!Typescript will automatically tell you that there is an error if you rename the property at some point.

请不要使用此问题的答案!如果您在某个时候重命名该属性,Typescript 会自动告诉您存在错误。



Original question (2014)

原始问题 (2014)

Objective

客观的

I have an interface TypeScript :

我有一个接口 TypeScript :

interface IInterface{
    id: number;
    name: string;
}

I have some methods which take in entry the name of a property (string).

我有一些方法可以输入属性的名称(字符串)。

Ex:

例如

var methodX = ( property: string, object: any ) => {
    // use object[property]
};

My problem is that when i call methodX, I have to write the property name in string.

我的问题是,当我打电话时methodX,我必须在字符串中写入属性名称。

Ex :methodX("name", objectX);where objectX implements IInterface

例如:methodX("name", objectX);objectX 实现 IInterface

But this is BAD: If i rename a property (let's say i want to rename nameto lastname) i will have to update manually all my code.

但这很糟糕:如果我重命名一个属性(假设我想重命名namelastname),我将不得不手动更新我的所有代码。

And I don't want this dependency.

我不想要这种依赖。

As typescript interfaces have no JS implementations, I don't see how I could not use string.

由于打字稿接口没有 JS 实现,我不知道我怎么不能使用字符串。

I want to have something like : methodX(IInterface.name.propertytoString(), objectX);

我想要类似的东西: methodX(IInterface.name.propertytoString(), objectX);

I'm pretty new to JS, do you see an alternative ?

我对 JS 很陌生,你有没有其他选择?

(Optional) More details :Why do I need to pass properties as parameter, and why I don't use a generic method ?

(可选)更多细节:为什么我需要将属性作为参数传递,为什么我不使用泛型方法?

I use methods that link data :

我使用链接数据的方法:

linkData = <TA, TB>(
    inputList: TA[],
    inputId: string,
    inputPlace: string,
    outputList: TB[],
    outputId: string ) => {

    var mapDestinationItemId: any = {};
    var i: number;
    for ( i = 0; i < outputList.length; ++i ) {
        mapDestinationItemId[outputList[i][outputId]] = outputList[i];
    }

    var itemDestination, itemSource;
    for ( i = 0; i < inputList.length; ++i ) {
        itemDestination = inputList[i];
        itemSource = mapDestinationItemId[itemDestination[inputId]];
        if ( itemSource ) {
            itemDestination[inputPlace] = itemSource;
        }
    }
};

But TA and TB can have a lot of different ids. So i don't see how to make it more generic.

但是 TA 和 TB 可以有很多不同的 ID。所以我不知道如何使它更通用。

采纳答案by Antoine ESTEVE

Update 2019: This answer is outdated, please look at the update added directly into the question.

2019 年更新:此答案已过时,请查看直接添加到问题中的更新。



basaratanswer is a good idea, but it doesn't work with interfaces.

basarat答案是个好主意,但它不适用于接口。

You can'twrite methodX(interfacePropertyToString(()=>interfaceX.porpertyname), objectX)because interfaceXis not an object.

不能写,methodX(interfacePropertyToString(()=>interfaceX.porpertyname), objectX)因为interfaceX它不是一个对象。

Interfaces are abstractions and they are used only for TypeScript, they doesn't exist in Javascript.

接口是抽象的,它们仅用于 TypeScript,它们不存在于 Javascript 中。

But thanks to his answer i found out the solution : using a parameter in the method.

但是由于他的回答,我找到了解决方案:在方法中使用参数

Finally we have :

最后我们有:

    interfacePropertyToString = ( property: (object: any) => void ) => {
        var chaine = property.toString();
        var arr = chaine.match( /[\s\S]*{[\s\S]*\.([^\.; ]*)[ ;\n]*}/ );
        return arr[1];
    };

We have to use [\s\S]to be able to match on multilines because Typescript convert (object: Interface) => {object.code;}to a multiline function.

我们必须使用[\s\S]才能在多行上进行匹配,因为 Typescript 转换(object: Interface) => {object.code;}为多行函数。

Now you can use it as you want :

现在你可以随意使用它:

        interfacePropertyToString(( o: Interface ) => { o.interfaceProperty});
        interfacePropertyToString( function ( o: Interface  ) { o.interfaceProperty});

回答by basarat

You could write a function to parse the body of a function to find the name e.g.:

您可以编写一个函数来解析函数体以查找名称,例如:

methodX(getName(()=>something.name), objectX)

Where getNamewill do a toStringon the function body to get a string of the form "function(){return something.name}"and then parse it to get "name".

WheregetNametoString在函数体上执行 a以获取表单的字符串,"function(){return something.name}"然后对其进行解析以获取"name".

Note: however this has a tendency to break depending upon how you minify it.

注意:然而,这有一种破坏的趋势,这取决于你如何缩小它。

回答by BrightShadow

I've changed basaratcode a little bit, so we can use it as generic:

我稍微改变了basarat代码,因此我们可以将其用作通用代码:

const P = <T>( property: (object: T) => void ) => {
    const chaine = property.toString();
    const arr = chaine.match( /[\s\S]*{[\s\S]*\.([^\.; ]*)[ ;\n]*}/ );
    return arr[1];
};

And example usage:

和示例用法:

console.log(P<MyInterface>(p => p.propertyName));

回答by plinyar

Somewhat related problem - how to get/set a value to a property path. I wrote two classes for that:

有点相关的问题 - 如何获取/设置属性路径的值。我为此写了两个类:

export class PropertyPath {
    static paths = new Map<string, PropertyPath>()

    static get<T, P>(lambda: (prop:T) => P) : PropertyPath {
        const funcBody = lambda.toString();
        var ret : PropertyPath = this.paths[funcBody];
        if (!ret) {
            const matches = funcBody.match( /(?:return[\s]+)(?:\w+\.)((?:\.?\w+)+)/ ); //first prop ignores
            var path = matches[1];
            ret = new PropertyPath(path.split("."));
            this.paths[funcBody] = ret;
        }
        return ret;
    };

    path : Array<string>

    constructor(path : Array<string>) {
        this.path = path
    }

    getValue( context : any) {
        const me = this;
        var v : any;
        return this.path.reduce( (previous, current, i, path) => {
            try {
                return previous[current];
            }
            catch (e) {
                throw {
                    message : `Error getting value by path. Path: '${path.join(".")}'. Token: '${current}'(${i})`,
                    innerException: e
                };
            }
        }, context)
    }

    setValue( context : any, value : any) {
        const me = this;
        var v : any;
        this.path.reduce( (previous, current, i, path) => {
            try {
                if (i == path.length - 1) {
                    previous[current] = value
                }
                return previous[current];
            }
            catch (e) {
                throw {
                    message : `Error setting value by path. Path: '${path.join(".")}'. Token: '${current}'(${i}). Value: ${value}`,
                    innerException: e
                };
            }
        }, context)
    }

}

Example of usage:

用法示例:

var p = PropertyPath.get((data:Data) => data.person.middleName)
var v = p.getValue(data)
p.setValue(data, newValue)


Some sugar over it:

加点糖:

export class PropertyPathContexted {

    static get<T, P>(obj : T, lambda: (prop:T) => P) : PropertyPathContexted {
        return new PropertyPathContexted(obj, PropertyPath.get(lambda));
    };

    context: any
    propertyPath: PropertyPath

    constructor(context: any, propertyPath: PropertyPath) {
        this.context = context
        this.propertyPath = propertyPath
    }

    getValue = () => this.propertyPath.getValue(this.context)

    setValue = ( value : any) => {this.propertyPath.setValue(this.context, value) }

}

And usage:

和用法:

var p = PropertyPathContexted.get(data, () => data.person.middleName)
var v = p.getValue()
p.setValue("lala")


I find the the latest quite convenient in two-way databinding in React:

我发现最新的 React 双向数据绑定非常方便:

var valueLink = function<T, P>( context: T, lambda: (prop:T) => P) {
    var p = PropertyPathContexted.get(context, lambda);
    return {
        value: p.getValue(),
        requestChange: (newValue) => {
            p.setValue(newValue);
        }
    }
};

render() {
   var data = getSomeData()
   //...
   return (
       //...
       <input name='person.surnames' placeholder='Surnames' valueLink={valueLink(data, () => data.person.surnames)}/>
       //...
   )
}