提取 属性 个名称的安全方法

Safe way to extract property names

我正在寻找一种通过类型检查获取对象 属性 名称的方法,该方法允许在重构后捕获可能的回归。

这是一个示例:我必须将 属性 名称作为字符串传递的组件,如果我尝试更改模型中的 属性 名称,它将被破坏。

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

如果有任何好的解决方案可以解决这个问题,我将不胜感激。

目前还没有真正好的方法,但目前有一些关于 github 的公开建议(参见 #1579, #394, and #1003)。

你能做的,就是this answer中显示的——在函数中包装引用属性,将函数转换为字符串,然后从中提取属性名称字符串。

这是执行此操作的函数:

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";

TS 2.1 中引入了 keyof 关键字,这使得这成为可能:

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

const propertiesOf = <TObj>(_obj: (TObj | undefined) = undefined) => <T extends keyof TObj>(name: T): T => name;

或使用Proxy

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

然后可以这样使用:

propertyOf<MyInterface>("myProperty");

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

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

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

如果 myProperty 不是 MyObj.

类型的 属性,这将给出错误

这是专门针对 React/React-Native 开发人员的。

为了安全地获得 属性-name,我使用下面的 class:

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') 获取 属性 名称。

您可以使用 keyofPick:

将 属性 名称提取为字符串
interface Test {
  id: number,
  title: string,
}

type TitleName = keyof Pick<Test, "title">;
     //^? type TitleName = "title"

const okTitle: TitleName = "title";
const wrongTitle : TitleName = "wrong";
     // Error: Type '"wrong"' is not assignable to type '"title"'

Playground