【问题标题】:Safe way to extract property names提取属性名称的安全方法
【发布时间】:2016-02-06 11:11:21
【问题描述】:

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

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

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#1003)。您可以查看this,但请注意,一旦代码被缩小,它可能无法正常工作。
  • @DavidSherret 您的this 解决方案也是我能想到的唯一答案。请张贴作为答案????

标签: typescript reflection metaprogramming


【解决方案1】:

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 类型的属性,这将产生错误。

【讨论】:

【解决方案2】:

目前还没有真正的好方法,但目前在 github 上有一些开放的建议(请参阅 #1579#394#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";

【讨论】:

  • 对于 a => a.property 我发现我需要从正则表达式中删除 '\}'
  • 我非常建议您为您的 非常棒的ts-nameof 包单独回答。在 hacky function-to-string 解决方案之间,它有点迷失了。或者只是删除那部分?无论哪种方式,您的帖子都不应该以它的方式开始,因为现在这样做的好方法。
  • 不要使用ts-nameof。作者已弃用它:github.com/dsherret/ts-nameof/issues/121
【解决方案3】:

这是专门为 React/React-Native 开发人员准备的。

为了安全地获取属性名,我使用下面的类:

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

并将extends React.Component&lt;PropTypes&gt;替换为extends BaseComponnent&lt;PropTypes

现在,您可以在Component 中调用this.getPropName('yourPropName') 来获取属性名称。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-28
    相关资源
    最近更新 更多