Безопасный способ извлечь имена свойств

Я ищу способ получить имя свойства объекта с помощью typechecking, что позволяет ловить возможные регрессии после рефакторинга.

Вот пример: компонент, где мне нужно передать имена свойств в виде строк, и он будет разбит, если я попытаюсь изменить имена свойств в модели.

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

Я был бы признателен за хорошее решение этой проблемы.

Ответ 1

В TS 2.1 было введено ключевое слово keyof, которое сделало это возможным:

const propertyOf = <TObj>(name: keyof TObj) => name;

или же

const propertyNamesOf = <TObj>(obj: TObj = null) => (name: keyof TObj) => name;

Затем их можно использовать так:

propertyOf<MyInterface>("myProperty");

или же

const myInterfaceProperties = propertyNamesOf<MyInterface>();
myInterfaceProperties("myProperty");

или же

const myInterfaceProperties = propertyNamesOf(myObj);
myInterfaceProperties("myProperty");

Это выдаст ошибку, если myProperty не является свойством типа MyObj.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

Ответ 2

Прямо сейчас нет отличного способа сделать это, но в настоящее время есть некоторые открытые предложения по github (см. # 1579, # 394 и # 1003).

Что вы можете сделать, это то, что показано в этом ответе, - ссылку на свойство в функции, преобразование функции в строку, а затем извлечение имени свойства строки.

Здесь используется функция:

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

Затем используйте его так:

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

Возможно, это не сработает в зависимости от того, как код был уменьшен, поэтому просто следите за этим.

Обновление

Это безопаснее делать это во время компиляции. Я написал ts-nameof, так что это возможно:

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

Скомпилируется:

"name";

Ответ 3

Это специально для разработчиков React/React-Native.

Чтобы безопасно получить property-name, я использую следующий класс:

export class BaseComponent<P = {}, S = {}> extends Component<P, S> {
  protected getPropName = (name: keyof P) => name;
  protected getStateName = (name: keyof S) => name;
}

И заменяет extends React.Component<PropTypes> на extends BaseComponnent<PropTypes,

Теперь с помощью Component вы можете вызвать this.getPropName('yourPropName') чтобы получить имя свойства.