Typescript通用状态更改函数类型转换错误

时间:2019-07-12 09:20:54

标签: reactjs typescript react-state

我正在创建一个通用函数来更改状态。

错误消息:

TS2345: Argument of type '() => { [x:string]: string }' is not assignable to parameter of type 'IUserSignUpState | Pick<IUserSignUpState, "url" | "errors"> | ((prevState: Readonly<IUserSignUpState>, props: Readonly<IUserSignUpState>) => IUserSignUpState | Pick<...> | null) null'.

Type '() => { [x: string] string; }' is not assignable to type '(prevState: Readonly<IUserSignUpState>, props: Readonly<IUserSignUpState>) => IUserSignupState | Pick<IUserSignUpState>, "url" | "errors" | null'. 

Type '{ [x: string]: string; }' is not assignable to type 'IUserSignUpState | Pick<IUserSignUpState, "url" | "errors"> | null'.

Type '{ [x: string]: string; }' is missing the following properties from type 'Pick<IUserSignUpState, "url" | "errors">': url, errors.

这是通用更改功能的示例:

./ UserSignUp.tsx


interface IUserSignUpState {
  url: string;
  errors: string[];
  // following can be used, but will lose type checking (*)
  // [key: string]: string;
}

class UserSignUp extends React.Component<{}, IUserSignUpState>
  state = {
    url: '',
    name: '',
    errors: [],
  };

  render() {
    const { url, errors } = this.state;
    return (
      <form>
        <input type="text" name="url" value={url} onChange={this.change} />
        <input type="text" name="name" value={name} onChange={this.change} />
      <form>
    )
  }

  change = (event:React.ChangeEvent<HTMLInputElement>) => {
    const name = event.target.name;
    const value = event.target.value;

    // gives error here:
    this.setState(() => {
      return {
        [name]: value
      }
    })
  }
};

在此示例中,更改事件仅应被允许更新url: string;name: string;,而不是errors: []

我应该定义“ url”和“ name”类型,然后以某种方式在更改功能中重用它们吗?


*在打字稿文档中,指出可以使用Indexable types。但是,这样做将丢失类型检查。而且还有一个副作用,就是我还可以设置“错误”状态,而更改功能通常是不可能的。


更新:根据此Stackoverflow Question中的答案,可能有以下解决方案:

拆分界面:

interface IInputState {
  url: string;
}

interface IUserSignUpState extends IInputState {
  errors: string[];
}

然后通过以下任一方式重新使用该界面:

this.setState({ [name]: value } as Partial<IInputStates>)

或:

const name = event.target.name as keyof IInputStates;
const value = event.target.value;

this.setState(():IInputStates => {
  return { [name]: value }
});

2 个答案:

答案 0 :(得分:1)

使用类型不会阻止errors属性更改,因为类型检查仅在编译时适用。当它到达浏览器时,仍然会像普通的旧动态JavaScript一样运行。

如果您想限制属性,则更改功能将作用于其中,您需要检查属性名称,例如针对允许值的数组。

请注意,您输入的内容缺少name属性。

 render() {
    const { url, errors } = this.state;
    return (
      <form>
        <input type="text" name="url" value={url} onChange={this.change} />
        <input type="text" name="name" value={name} onChange={this.change} />
      <form>
    )
  }

  change = (event:React.ChangeEvent<HTMLInputElement>) => {
    const name = event.target.name;
    const value = event.target.value;

    if (!isValidProperty(name)) return;

    this.setState({ [name]: value } as Partial<IInputStates>)
  }

  // Dummy instance to force a compile warning and ensure we capture all the property names
  const inputExample: IInputStates = { url: '' };
  const inputKeys = Object.keys(inputExample);

  function isValidProperty(name: string): name is keyof IInputStates {
    return inputKeys.includes(name);
  }

答案 1 :(得分:0)

有一些解决方案:

 this.setState<never>({ [name]: value } as Partial<IInputStates>)

 this.setState({ [name]: value } as Pick<IInputStates, keyof IInputStates>);

git中有一个未解决的问题,看看: cannot setState with dynamic key name type-safe