【问题标题】:Inferring union of array values does not work as expected推断数组值的联合不能按预期工作
【发布时间】:2020-03-26 04:51:57
【问题描述】:

我今天正在研究一种从两个字符串列表创建键值映射的方法。这是一个例子:

const keys = ['a', 'b', 'c']
const values = ['x', 'y', 'z']
const map = createMap(keys, values)
/*
{
  a: 'x',
  b: 'y',
  c: 'z'
}
*/

我登陆的实现如下所示:

function createMap<T extends string>(
  keys: readonly T[],
  values: readonly string[]
): Record<T, string> {
  if (keys.length !== values.length) {
    throw new Error('Key and Value lists must have same length') 
  }

  return keys.reduce<Record<string, string>>((accumulator, key, index) => {
    if (accumulator.hasOwnProperty(key)) {
      throw new Error('Keys must be unique')
    }

    return {
      ...accumulator,
      [key]: values[index]
    }
  }, {})
}

它可以工作,但是类型推断有一个奇怪的属性

key 参数是包含字符串数组的变量时,结果为:Record&lt;string, string&gt;,但如果您直接将数组传递给key 参数,则结果为:Record&lt;'item1' | 'item2' | 'etc.', string&gt;。查看下面的代码了解更多详情:

const keys = ['a', 'b', 'c']
const values = ['x', 'y', 'z']
const map = createMap(keys, values) // type is Record<string, string>
const map = createMap(['a', 'b', 'c'], values) // type is Record<'a' | 'b' | 'c', string>

有人能解释一下为什么会这样吗?

这是TypeScript Playground中此代码的链接

【问题讨论】:

    标签: typescript ecmascript-6


    【解决方案1】:

    这似乎是因为 ['a', 'b', 'c'] 字面量的类型被推断为元组类型 ['a', 'b', 'c'],当用作数组时变为 Array&lt;'a' | 'b' | 'c'&gt;。相反,您的keys 变量被推断为Array&lt;string&gt; 类型。

    您可以通过显式类型注释来实现相反的结果:

    const keys: ['a', 'b', 'c'] = ['a', 'b', 'c']
    const values = ['x', 'y', 'z']
    
    const m1 = createMap(keys, values) // => Record<'a' | 'b' | 'c', string>
    const m2 = createMap(['a', 'b', 'c'] as string[], values) // => Record<string, string>
    

    【讨论】:

    • 替代const keys = ['a', 'b', 'c'] as const。一种将数组推断为元组的重复性略低的方法。
    • 嗯,我看到了非常有趣的行为
    【解决方案2】:

    在第一个示例中,keys 参数的类型是 string[] 类型,因此 Tstring 类型,因此返回类型变为 Record&lt;string, string&gt;

    在第二个示例中,keys 参数是字符串文字类型的元组。

    在此处查看有关元组的文档:https://www.typescriptlang.org/docs/handbook/basic-types.html#tuple

    在此处查看有关字符串文字类型的文档:https://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types

    请参阅下面的示例以更明确地了解正在发生的事情:

    const keys: ['a', 'b', 'c'] = ['a', 'b', 'c']; // Type is tuple of string literal types
    const map = createMap(keys, values) // type is Record<'a' | 'b' | 'c', string>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-04
      • 2015-06-26
      相关资源
      最近更新 更多