【问题标题】:TypeScript: Typing a value based on an object keyTypeScript:基于对象键键入值
【发布时间】:2022-02-08 23:48:20
【问题描述】:

我需要一些帮助来完成我的 TypeScript 冒险。

这是我的示例类型:

type Target = {
  names: string[]
  addresses: {
      location: string
    }[]
};

这是我的示例对象:

const myTarget: Target = {
  names: ["Alex", "Nico"],
  addresses: [
    {
      location: "Miami",
    },
    {
      location: "New York",
    },
  ],
};

这是我的函数示例:

const changeValue = (
    key: keyof Target,
    value: string | Record<string, unknown>
) => {
 myTarget[key] = [...myTarget[key], value]
}

changeValue('names', 'Elvis')
changeValue('addresses', { location: "Atlanta" })

我在myTarget[key] 上有一个错误:

无法将类型 '(string | Record)[]' 分配给类型 'string[] & { location: string; }[]'。
无法将类型 '(string | Record)[]' 分配给类型 'string[]'。
无法分配类型“字符串 | “字符串”类型的记录'。
无法将类型 'Record' 分配给类型 'string' .ts(2322)

据我了解,例如,TypeScript 正在测试所有组合,包括将对象添加到无效的 names 数组中,或者在 addresses 数组中添加字符串。

有人可以帮助我了解问题所在以及我该如何处理。 (真正的项目要复杂得多,但也许通过这个例子我会明白一点..)

非常感谢。

【问题讨论】:

    标签: javascript arrays typescript object types


    【解决方案1】:

    一般来说,如果您想传递对象类型的键和该键类型的值,您可以这样做:

    const changeValue = <K extends keyof Target, V extends Target[K]>(
        key: K,
        value: V
    ) => {
     myTarget[key] = value; 
    }
    

    也就是说,你告诉编译器value不仅是对象的某个值的同一类型,而且与key给出的类型相同。

    但是,您通过玩数组提高了难度。据我所知,没有一个实用程序类型来提取数组的类型,所以我写了一个:

    type ArrayType<T> = T extends Array<infer U>? U : never;
    

    现在我们可以使用它了:

    const appendValue = <K extends keyof Target, V extends ArrayType<Target[K]>>(
        key: K,
        value: V
    ) => {
     myTarget[key] = [...myTarget[key], value] as Target[K];
    }
    

    不幸的是,如您所见,编译器对V[]Target[K] 之间的关系有点困惑,因此它需要as 形式的提示。

    现在调用编译干净了:

    appendValue('names', 'Elvis')
    appendValue('addresses', { location: "Atlanta" })
    

    顺便说一句,您在示例代码中做了一些我建议您不要做的事情(尽管可能不在您的实际代码中)。

    首先,您有mutable global datamyTargetHic fons lacrimis,如果可以避免的话,不要用任何语言这样做。

    另外,变异数据通常是a bad idea。请考虑一下:

    const appendValue = <K extends keyof Target, V extends ArrayType<Target[K]>>(
        target: Target,
        key: K,
        value: V
    ): Target => ({
        ...target,
        [key]: [...myTarget[key], value] as Target[K]
    })
    

    【讨论】:

    • 谢谢。这是你使用的很多新概念,但我会尽量适应它们。
    【解决方案2】:

    为什么会出错?

    这个问题的答案你可以在hereherehere和我的文章here中找到。

    value 参数使用这种类型的string | Record&lt;string, unknown&gt; 是不安全的。最好推断key参数的类型:

    const changeValue = <Key extends keyof Target>(
        key: Key,
        value: Target[Key][number]
    ) => {
        // error
        myTarget[key] = [...myTarget[key], value]
    }
    

    这里有一个错误,因为keyvalue 之间没有关联(内部)函数体。这种相关性只存在于函数之外,我的意思是,当你调用这个函数时。

    请记住,key 仍然是联合类型。 我认为值得使用不可变的方法:

    type Target = {
        names: string[]
        addresses: {
            location: string
        }[]
    };
    
    const myTarget: Target = {
        names: ["Alex", "Nico"],
        addresses: [
            {
                location: "Miami",
            },
            {
                location: "New York",
            },
        ],
    };
    
    
    const withTarget = <
        Obj extends Record<string, unknown[]>
    >(target: Obj) => <
        Key extends keyof Obj
    >(
        key: Key,
        value: Obj[Key][number]
    ): Obj => ({
        ...target,
        [key]: target[key].concat(value)
    })
    
    const changeValue = withTarget(myTarget);
    const result = changeValue('names', 'Elvis')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-14
      • 1970-01-01
      • 2022-11-19
      • 2018-12-29
      • 1970-01-01
      • 2019-04-03
      • 2021-09-12
      • 2021-07-15
      相关资源
      最近更新 更多