ReactJS and TypeScript. My overall objective is to create a single event handler function that takes in the name of the input element (select, input, etc) and sets the property of a complex object based on the property name along with the value from the event.
In essence, something like this:
handleChange = idx => e => {
const { name, value } = e.target;
const rows = [...this.state.rows];
rows[idx] = {
[name]: value
};
this.setState({
rows
});
};
However, in my case, I'm using TypeScript, and I'm also using a fairly complex object.
Here is some test code:(reference Using Type Parameters in Generic Constraints)
export interface ITraveler {
id: number;
dob: Date;
tripCost: number;
firstName: string;
lastName: string;
}
export enum CoverageType {
Standard,
Plus,
}
export default interface IQuoteInfo {
country: string;
state: { fullname: string; abbreviation: string };
travelDates: {
fromDate: Date;
toDate: Date;
};
travelerInfo: ITraveler[];
paymentInfo: {
initialDate: Date;
finalDate: Date | null;
};
coverageType: CoverageType;
tripInfo: {
name: string;
program: string;
};
contactInfo: {
firstName: string;
lastName: string;
address1: string;
address2: string;
city: string;
state: string;
zip: string;
country: string;
phone: string;
email: string;
};
setQuoteInfo: (e: any) => void;
}
const initialQuoteInfo: IQuoteInfo = {
country: "USA",
state: {
fullname: "New York",
abbreviation: "NY",
},
travelDates: {
fromDate: new Date(2020, 10, 28),
toDate: new Date(2020, 10, 30),
},
travelerInfo: [
{ id: 1, dob: new Date(), tripCost: 0, firstName: "", lastName: "" },
],
paymentInfo: {
initialDate: new Date(),
finalDate: null,
},
coverageType: CoverageType.Standard,
tripInfo: {
name: "",
program: "Program Name",
},
contactInfo: {
firstName: "",
lastName: "",
address1: "",
address2: "",
city: "",
state: "",
zip: "",
country: "",
phone: "",
email: "",
},
setQuoteInfo: () => {},
};
const getProperty = <T, K extends keyof T>(obj: T, key: K) => {
return key;
};
const setProperty = <T, K extends keyof T>(obj: T, key: K, value: any) => {
obj[key] = value;
console.log("object is", obj);
};
setProperty(initialQuoteInfo, "country", "United Kingdom");
Okay, so what's my problem? This works, but I'm at a loss as to how I'd set the property and value for anything deeper or more complex than a key/value property. For example, how would I set the state
fullname
and abbreviation
? Assuming that the element "name" would be "state.fullname", I would try to do this:
setProperty(initialQuoteInfo, "state.fullName", "Virginia");
TypeScript complains:
Argument of type '"state.fullName"' is not assignable to parameter of type '"contactInfo" | "country" | "state" | "travelDates" | "travelerInfo" | "paymentInfo" | "coverageType" | "tripInfo" | "setQuoteInfo"'.ts(2345)
This works:
setProperty(initialQuoteInfo, "state", {
fullname: "Virginia",
abbreviation: "VA",
});
There are a couple of ways I can think of to manage this off-hand. One would be to parse the "name" and if it has a dot, then do some kind of funky setting. Another might be to create another function that takes in the parsed name and does essentially the same thing as the setProperty
function.
I can't help but think there might be a more elegant and functional methodology, so I'd appreciate any assistance.