Обобщенное изменение State в TypeScript
Подскажите пожалуйста, казалось бы вопрос простой и на JS его легко сделать. А вот как перевести на TypeScript не понимаю.
Имеется интерфейс
interface ILoginUser {
Email: string;
Password: string;
}
пишу обобщенный метод, который бы можно было применить к двум Input. Сейчас это два метода:
class Login extends React.Component<{}, ILoginUser> {
public state: ILoginUser = {
Email: "",
Password: "",
};
public ChangeEmail = (e: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
Email: e.target.value,
});
};
public ChangePassword = (e: React.ChangeEvent<HTMLInputElement>): void => {
this.setState({
Password: e.target.value,
});
};
render{}
И вот их хочу объединить в один чтобы не плодить копипаст. На JS это делается в пару строчек:
private handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { name , value } = event.target;
this.setState({
[name]: value
});
}
Ошибка на этой строке:
this.setState({ [name]: value });
Сам текст ошибки:
TS2345: Argument of type '{ [x: string]: string; }' is not assignable to parameter of type 'ILoginUser | Pick<ILoginUser, "Email" | "Password"> | ((prevState: Readonly, props: Readonly<{}>) => ILoginUser | Pick<ILoginUser, "Email" | "Password"> | null) | null'. Type '{ [x: string]: string; }' is missing the following properties from type 'Pick<ILoginUser, "Email" | "Password">': Email, Password
Но TypeScript не даст так просто такую манипуляцию провести без типизирования. И вот как это типизировать правильно я не знаю. Буду благодарен за помощь.
Ответы (1 шт):
Это происходит из-за того, что у вас используются разные типы, хотя казалось бы, что они полность совместимы. Допустим у вас state объявлен как ILoginUser, то TypeScript будет от вас требовать полный тип ILoginUser или его "неполную" версию (Pick).
Но в вашем коде this.setState({ [name]: value }); вы передаете некий объект, который имеет тип { [x: string]:string }. Т.е. компилятор не знает, что у вас в качестве name будут строго Email | Password, с его стороны туда может быть передана любая строка, поэтому он и не разрешает такой код.
Это можно поправить явным приведением типов:
private handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { name, value } = event.target;
const newState = { [name]: value } as Pick<ILoginUser, keyof ILoginUser>;
this.setState(newState);
}
или при помощи (оператора ...):
private handleInputChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { name, value } = event.target;
this.setState({ ...this.state, [name]: value });
}