Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript interface property to string

Question/Answer - Update 2021

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

If you want the type of a variable to be a property of another one, you can use keyof.

Example:

interface User {
    name: string;
    age: number;
}

const nameProperty: keyof User = 'name'; // ok
const ageProperty: keyof User = 'age'; // ok
const emailProperty: keyof User = 'email'; // not ok

If you want a method that takes a parameter which is a property of another parameter you can use generics to link both types together.

Example using generics + keyof:

const foo = <TObject extends object>(
    object: TObject,
    property: keyof TObject
) => {
    // You can use object[property] here
};

foo({ a: 1, b: 2 }, 'a'); // ok
foo({ a: 1, b: 2 }, 'b'); // ok
foo({ a: 1, b: 2 }, 'c'); // not ok

Example using generics + Record:

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

foo({ a: 1, b: 2 }, 'a'); // ok
foo({ a: 1, b: 2 }, 'b'); // ok
foo({ a: 1, b: 2 }, 'c'); // not ok

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.


Original question (2014)

Objective

I have an interface 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.

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

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

And I don't want this dependency.

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

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

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

(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.

like image 672
antoinestv Avatar asked Apr 13 '15 08:04

antoinestv


2 Answers

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


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

You can't write methodX(interfacePropertyToString(()=>interfaceX.porpertyname), objectX) because interfaceX is not an object.

Interfaces are abstractions and they are used only for TypeScript, they doesn't exist in 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.

Now you can use it as you want :

        interfacePropertyToString(( o: Interface ) => { o.interfaceProperty});
        interfacePropertyToString( function ( o: Interface  ) { o.interfaceProperty});
like image 105
antoinestv Avatar answered Oct 08 '22 12:10

antoinestv


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

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

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

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

like image 20
basarat Avatar answered Oct 08 '22 11:10

basarat