Typescript interface property for string

goal

I have a TypeScript interface:

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

I have several methods that take the name of the 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 name of the property in a string.

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

But this is bad . If I rename a property (say, I want to rename name to lastname ), I will have to manually update all my code.

And I do not want this addiction.

Since TypeScript interfaces do not have JS implementations, I cannot see how I could not use the string.

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

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

(optional) Additional information: Why do I need to pass properties as a parameter and why am I not using a generic method?

I use methods that bind 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 many different identifiers. Therefore, I do not see how to make it more general.

+5
source share
3 answers

basarat's answer is a good idea, but it does not work with interfaces.

You cannot write methodX(interfacePropertyToString(()=>interfaceX.porpertyname), objectX) because interfaceX not an object.

Interfaces are abstractions, and they are used only for TypeScript, they do not exist in Javascript.

But thanks to his answer, I found a solution: using the 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 must use [\s\S] to be able to match on multi-lines, because Typescript convert (object: Interface) => {object.code;} into a multi-line function.

Now you can use it however you want:

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

You can write a function to parse the function body to find a name, for example:

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

Where getName will do a toString in the function body to get the form string "function(){return something.name}" and then parse it to get the "name" .

Note: however, this tends to break depending on how you minimize it.

+2
source

A related issue is how to get / set the value for the property path. I wrote two classes for this:

 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) } } 

Usage example:

 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 use:

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

I find the latter quite convenient in two-way data binding in 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)}/> //... ) } 
0
source

All Articles