打字稿以编程方式设置复杂对象的属性值

时间:2020-10-27 14:11:18

标签: javascript reactjs typescript

ReactJS和TypeScript。我的总体目标是创建一个单一的事件处理函数,该函数接受输入元素的名称(选择,输入等),并根据属性名称以及事件的值来设置复杂对象的属性。

本质上,类似this

  handleChange = idx => e => {
    const { name, value } = e.target;
    const rows = [...this.state.rows];
    rows[idx] = {
      [name]: value
    };
    this.setState({
      rows
    });
  };

但是,就我而言,我正在使用TypeScript,并且我还使用了一个相当复杂的对象。

以下是一些测试代码:(参考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");

好的,那我有什么问题?这行得通,但是我对于如何设置比键/值属性更深或更复杂的属性和值感到困惑。例如,如何设置state fullnameabbreviation?假设元素“名称”为“ state.fullname”,我将尝试这样做:

setProperty(initialQuoteInfo, "state.fullName", "Virginia");

TypeScript抱怨:

类型'“ state.fullName”'的参数不可分配给的参数 类型'“ contactInfo” | “国家” | “状态” | “旅行日期” | “ travelerInfo” | “ paymentInfo” | “ coverageType” | “ tripInfo” | “ setQuoteInfo”'。ts(2345)

这有效:

setProperty(initialQuoteInfo, "state", {
  fullname: "Virginia",
  abbreviation: "VA",
});

我可以通过几种方法来管理此临时任务。一种方法是解析“名称”,如果它有一个点,则进行某种时髦的设置。另一个可能是创建另一个函数,该函数采用已解析的名称,并且与setProperty函数具有本质上相同的作用。

我忍不住想,也许会有一种更加优雅和实用的方法论,所以我将不胜感激。

1 个答案:

答案 0 :(得分:1)

function setProperty(obj: object, key: string, value: any) {
    const propertyPath = key.split(".");
    const propertyName = propertyPath.pop();
    const baseObj = propertyPath.reduce((t: any, c) => t[c], obj);
    baseObj[propertyName] = value;
}

经过测试:

setProperty(initialQuoteInfo, "country", "United Kingdom");
setProperty(initialQuoteInfo, "state.fullname", "Virginia");
setProperty(initialQuoteInfo, "coverageType", CoverageType.Standard);
setProperty(initialQuoteInfo, "travelerInfo.0.dob", new Date(1980, 1, 1));

我承认它不符合您的“优雅且实用”的标准,但它确实可以满足您的要求,而且很容易理解。