首页 > 解决方案 > 打字稿以编程方式设置复杂对象属性值

问题描述

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

本质上,是这样的:

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

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

这是一些测试代码:(参考在通用约束中使用类型参数

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 fullnameand abbreviation?假设元素“name”是“state.fullname”,我会尝试这样做:

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

打字稿抱怨:

'"state.fullName"' 类型的参数不可分配给 '"contactInfo" 类型的参数 | “国家” | “状态” | “旅行日期” | "旅行者信息" | "支付信息" | “覆盖类型” | "旅行信息" | "setQuoteInfo"'.ts(2345)

这有效:

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

我可以想到几种方法来临时管理这个问题。一种是解析“名称”,如果它有一个点,那么做一些时髦的设置。另一个可能是创建另一个函数,该函数接受解析后的名称,并与函数本质上做同样的事情setProperty

我不禁认为可能有一种更优雅、更实用的方法,因此我将不胜感激。

标签: javascriptreactjstypescript

解决方案


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));

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


推荐阅读