如何在TypeScript中迭代异构对象

huangapple go评论62阅读模式
英文:

How to Iterate through heterogenous object in Typescript

问题

  1. 在这种情况下,有办法帮助 TypeScript 正确推断类型吗?
  2. 有没有更好的方法来实现我想要完成的任务?

【代码部分不进行翻译】

英文:

I have a state object and an update object that will be combined with the state object, though null in the update means delete so I can't just do {...a, ...b} with them.

const obj = {
    other: new Date(),
    num:5,
    str:"original string"
}

const objUpdate:Partial<typeof obj> = {
    num:6,
    str:"updated string"
}

The task: Iterate through the update object and apply its values to the original object. This is how I ideally would do it:

Object.entries(objUpdate).forEach(([k,v])=>{
    if (v === undefined) return;
    if (v === null){
        delete obj[k]; // <-
        return;
    }
    obj[k] = v; // <-
})

But at the indicated lines I get an error that No index signature with a parameter of type 'string' was found on type '{ other: Date; num: number; str: string; }'. Ideally Typescript would know that k is already keyof typeof objUpdate (edit: here's possibly why) but I guess I can explicitly indicate that:

Object.entries(objUpdate).forEach(([k,v])=>{
    if (v === undefined) return;
    if (v === null){
        delete obj[k as keyof typeof objUpdate];
        return;
    }
    obj[k as keyof typeof objUpdate] = v; // <-
})

At the indicated line it complains that Type 'string | number | Date' is not assignable to type 'never'. Type 'string' is not assignable to type 'never'.

  1. Is there a way to help Typescript infer typings correctly in this situation?
  2. Is there a better approach to what I am trying to accomplish?

答案1

得分: 1

在JavaScript中,动态删除对象上的键既慢又可能会让TypeScript的类型检查更加困难。最简单的方法是调整代码中引用状态的其他部分,以便它们检查值是否为 null。因此,不要像这样拥有一个状态对象:

const obj = {
    other: <someDate>,
    str: <someString>
}

而应该是这样的:

const obj = {
    other: <someDate>,
    num: null,
    str: <someString>
}

使用这种方法,类型检查将正常工作,状态更新将像 {...a, ...b} 一样简单。

为了为初始状态定义类型,将键映射到一个带有 null 的新对象类型中。

const obj = {
    other: new Date(),
    num: 5,
    str: "original string"
}
type Obj = typeof obj;
type ObjState = {
  [K in keyof Obj]: Obj[K] | null;
};

// ...

const [stateObj, setStateObj] = useState<ObjState>(obj);
// 更新:
setStateObj({ ...stateObj, ...objUpdate });

如果代码的某部分要求对象在格式化时去除 null 属性(例如用于数据库查询),请在必要时创建一个新对象,然后再发送该对象,而不是更改状态的形状。

const objWithNullPropertiesRemoved: Partial<Obj> = Object.fromEntries(
  Object.entries(stateObj)
    .filter(([, val]) => val !== null)
);
// 将 objWithNullPropertiesRemoved 发送到某个地方
英文:

Dynamically deleting keys on an object is both slow in JavaScript and can make typing more difficult in TypeScript. The easiest way to approach this by a good margin would be to tweak the other parts of your code that reference the state so that they check if the value is null or not. So, instead of having a state object of

const obj = {
    other: &lt;someDate&gt;,
    str: &lt;someString&gt;
}

there would be

const obj = {
    other: &lt;someDate&gt;,
    num: null,
    str: &lt;someString&gt;
}

Using this approach, typing will just work, and the state update will be as trivial as {...a, ...b}.

To type the initial state, map the keys to a new object type with null added in.

const obj = {
    other: new Date(),
    num:5,
    str:&quot;original string&quot;
}
type Obj = typeof obj;
type ObjState = {
  [K in keyof Obj]: Obj[K] | null;
};

// ...

const [stateObj, setStateObj] = useState&lt;ObjState&gt;(obj);
// Update:
setStateObj({ ...stateObj, ...objUpdate });

If some part of the code requires the object to be formatted with the null properties removed (such as for a database query), do that by creating a new object when it's necessary, just before sending the object, rather than changing the state's shape.

const objWithNullPropertiesRemoved: Partial&lt;Obj&gt; = Object.fromEntries(
  Object.entries(stateObj)
    .filter(([, val]) =&gt; val !== null)
);
// send the objWithNullPropertiesRemoved somewhere

答案2

得分: 0

我接受的答案很可能是大多数人正确的方法,但我刚刚找到了一种使我的原始方法工作并在这里提供的方法。

使用https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type,然后将值v键入as UnionToIntersection&lt;typeof obj[keyof typeof obj]&gt;

英文:

The answer I accepted is most likely the correct approach for most people but I just found a way to get my original approach to work and provide it here for completeness.

Using https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type I then typed the value v as as UnionToIntersection&lt;typeof obj[keyof typeof obj]&gt;

huangapple
  • 本文由 发表于 2023年2月16日 04:20:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/75465085.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定