shadeglare shadeglare - 1 month ago 9
TypeScript Question

Safe way to extract property names in TypeScript

I'm looking for a way to get an object property name with typechecking that allows to catch possible regressions after refactoring.

Here's an example: the component where I have to pass the property names as strings and it will be broken if I'll try to change the property names in the model.

interface User {
name: string;
email: string;
}

class View extends React.Component<any, User> {

constructor() {
super();
this.state = { name: "name", email: "email" };
}

private onChange = (e: React.FormEvent) => {
let target = e.target as HTMLInputElement;
this.state[target.id] = target.value;
this.setState(this.state);
}

public render() {
return (
<form>
<input
id={"name"}
value={this.state.name}
onChange={this.onChange}/>
<input
id={"email"}
value={this.state.email}
onChange={this.onChange}/>
<input type="submit" value="Send" />
</form>
);
}
}


I'd appreciate if there's any nice solution to solve this issue.

Answer

Right now there's not really a great way of doing this, but there are currently some open suggestions on github (See #1579, #394, and #1003).

What you can do, is what's shown in this answer—wrap referencing the property in a function, convert the function to a string, then extract the property name out of the string.

Here's a function to do that:

function getPropertyName(propertyFunction: Function) {
    return /\.([^\.;]+);?\s*\}$/.exec(propertyFunction.toString())[1];
}

Then use it like so:

// nameProperty will hold "name"
const nameProperty = getPropertyName(() => this.state.name);

This might not work depending on how the code is minified so just watch out for that.

Update

It's safer to do this at compile time. I wrote ts-nameof so this is possible:

nameof<User>(s => s.name);

Compiles to:

"name";