如何根据打字稿中的字符串输入更新对象数据?

How to update object data based on string input in typescript?

我想根据与对象键相关的字符串的输入来更新对象的值,我应该如何使用打字稿来处理这个问题?

const objData = { // random value
  A: 11,
  B: 13,
  C: 53,
  innerObj: {
    AStatus: true,
    BStatus: false,
    CStatus: true,
  },
};

type Item = 'itemA' | 'itemB' | 'itemC';

function processObj(item: Item, obj: typeof objData) {
  if (item === 'itemA') { // <- duplicate
    obj.A = 5;
    obj.innerObj.AStatus = true;
    return obj;
  }

  if (item === 'itemB') { // <- duplicate
    obj.B = 5;
    obj.innerObj.BStatus = true;
    return obj;
  }

  if (item === 'itemC') { // <- duplicate
    obj.C = 5;
    obj.innerObj.CStatus = true;
    return obj;
  }
  return
}

processObj('itemA', objData);

例如,输入是 itemA,只有 A 个相关的 objData 被更新。

Typescript playground

如果 item 参数与 objobj.innerObj 的键名之间的关系是通过字符串操作以编程方式确定的,这样就有一个 属性 键objk对应obj.innerObj的属性k+"Status"item的值"item"+k,那么就可以重构了您的 processObj 函数使用 template literal types。模板文字类型允许您在类型级别表示一些字符串文字操作。这是一种方法:

type Keys = Exclude<keyof typeof objData, "innerObj">;
// type Keys = "A" | "B" | "C"

function processObj(item: `item${Keys}`, obj: typeof objData) {
  const k = item.substring(4) as Keys; // need to assert here 
  obj[k] = 5;
  obj.innerObj[`${k}Status`] = true;
  return obj;
}

Keys类型使用the Exclude<T, U> utility type to filter the keys of objData to remove "innerObj", leaving us with the union"A" | "B" | "C".

对于其余部分,我们使用模板文字类型在类型级别执行字符串连接。 item 的类型是 `item${Keys}`,计算结果为 "itemA" | "itemB" | "itemC"。我们可以通过从 item 中剥离初始的 "item" 前缀来计算 k;结果是 Keys 类型,但编译器无法验证这一点。因此我们只是 assert k 是类型 Keys.

我们可以只设置 obj[k] = 5 而没有编译器警告,因为编译器理解 objKeys 中的所有键处都有一个 number 属性。我们也可以设置 obj.innerObj[`${k}Status`] = true 而没有编译器警告,因为编译器知道 template literal string value 可以有一个模板文字 type,并且 [= 的类型41=] 是 `${Keys}Status`,计算结果为 "AStatus" | "BStatus" | "CStatus"。并且编译器知道 obj.innerObj 在这些键上有一个 boolean 属性。

所以这一切都按预期工作。


另一方面,如果 itemobjobj.innerObj 的键之间的关系是任意的,那么您不一定可以使用字符串操作来映射它们.在这种情况下,您可能需要诸如查找 table 之类的东西来表示映射,无论它是什么。这样的实现可能如下所示:

const propLookup = {
  itemA: "A",
  itemB: "B",
  itemC: "C"
} as const;
const statusLookup = {
  itemA: "AStatus",
  itemB: "BStatus",
  itemC: "CStatus"
} as const;
type Keys = keyof typeof propLookup;
// type Keys = "A" | "B" | "C"

function processObj(item: Keys, obj: typeof objData) {
  obj[propLookup[item]] = 5;
  obj.innerObj[statusLookup[item]] = true;
  return obj;
}

propLookupstatusLookup 对象只是从有效 item 值到 objobj.innerObj 的相应属性的映射。他们正在使用 const assertions so the compiler keeps track of the string literal types 的值。如果我们不使用 as const,那么编译器只会推断出 string 的值,这对我们没有帮助。

这也符合预期;编译器理解 propLookup[item] 是具有 number 值的 obj 的键,而 statusLookup[item] 是具有 booleanobj.innerObj 的键值。

Playground link to code