typescript - Mod sigur de a extrage numele proprietăților


Caut o modalitate de a obține numele unei proprietăți a unui obiect cu verificare a tipului care să permită detectarea eventualelor regresii după refactorizare.

Iată un exemplu: componenta în care trebuie să transmit numele proprietăților ca șiruri de caractere și se va strica dacă voi încerca să schimb numele proprietăților în 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>
      );
   }
}

Aș aprecia dacă ar exista vreo soluție bună pentru a rezolva această problemă.

Răspuns #1

În TS 2.1 a fost introdus cuvântul cheie keyof care a făcut acest lucru posibil:

function propertyOf<TObj>(name: keyof TObj) {
    return name;
}

sau

function propertiesOf<TObj>(_obj: (TObj | undefined) = undefined) {
    return function result<T extends keyof TObj>(name: T) {
        return name;
    }
}

sau utilizând Proxy

export function proxiedPropertiesOf<TObj>(obj?: TObj) {
    return new Proxy({}, {
        get: (_, prop) => prop,
        set: () => {
        throw Error('Set not supported');
        },
    }) as {
        [P in keyof TObj]?: P;
    };
}

Acestea pot fi apoi folosite astfel:

propertyOf<MyInterface>("myProperty");

sau

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

sau

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

sau

const myInterfaceProperties = proxiedPropertiesOf<MyInterface>();
myInterfaceProperties.myProperty;

sau

const myInterfaceProperties = proxiedPropertiesOf(myObj);
myInterfaceProperties.myProperty;

Răspunsul #2

În acest moment nu există cu adevărat o modalitate bună de a face acest lucru, dar în prezent există câteva sugestii deschise pe GitHub (vezi #1579, #394 și #1003).

Ceea ce poți face este ceea ce este arătat în acest răspuns—înfășoară referința la proprietate într-o funcție, convertește funcția într-un șir, apoi extrage numele proprietății din șir.

Iată o funcție pentru a face asta:

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

Apoi folosește-l astfel:

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

Acest lucru s-ar putea să nu funcționeze în funcție de modul în care este minificat codul, așa că doar fii atent la asta.

Actualizare

Este mai sigur să faci asta la timpul de compilare. Am scris ts-nameof pentru ca acest lucru să fie posibil:

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

Se compilează în:

"name";