【问题标题】:Can a function return a mapped tuple without noUncheckedIndexedAccess errors?函数可以返回没有 noUncheckedIndexedAccess 错误的映射元组吗?
【发布时间】:2021-03-14 00:51:27
【问题描述】:

下面的代码示例无法使用 noUncheckedIndexedAccess 进行编译。虽然已知literalValue 是一个数字,但valuespreadValue 变量在它们的联合类型中具有“未定义”。

有没有办法注释 squareTuple 和 squareSpreadTuple 函数,以便传递特定元组的元组将返回一个可以像字面元组一样解构的结果,而不会在其类型联合中出现虚假的未定义?

示例代码中的 Mapped 类型试图做到这一点(通过将元组的长度属性传递给它的返回值)。好像没什么效果。

还有什么其他方法可以以解构会尊重的方式将参数元组的数量转移到它的返回值?

function square(num:number){
  return num*num;
}

type Mapped<NumList extends ReadonlyArray<number>> = ReadonlyArray<number> & {length:NumList["length"]}

function squareTuple<NumList extends ReadonlyArray<number>>(nums:NumList): Mapped<NumList> {
  return nums.map(square);
}

function squareSpreadTuple<NumList extends ReadonlyArray<number>>(...numList:NumList) : Mapped<NumList> {
  return squareTuple(numList)
}

const [literalValue] = [9,16,25] as const;
const [value] = squareTuple([3,4,5] as const) ;
const [spreadValue] = squareSpreadTuple(3,4,5) ;

console.log(`Sum of values is: ${literalValue + value + spreadValue }`)

示例代码(带有可见的编译错误)位于this playground

更新

我使用this playground 的代码设法让红线消失了。对于我希望推断出的内置类型的东西,这绝不是优雅的。可能有更好的方法。

更新 2

如果我只将支持扩展到扩展案例,那么this playground 的更简单的方法可以工作。不过,我发现很难解释为什么非扩展案例无法使用这种更简单的方法进行编译。

【问题讨论】:

    标签: typescript tuples undefined mapped-types


    【解决方案1】:

    我倾向于这样写Mapped

    type Mapped<L extends readonly number[]> = { readonly [K in keyof L]: number }
    

    这是使用 TypeScript 对 using mapped types to turn arrays/tuples into arrays/tuples 的支持。这样的映射已经保留了length 属性,而无需您特别做任何事情。大概你试图对Mapped 做的所有事情就是撤消任何numeric literal type 推理并扩大回number。这就是上面的Mapped 所做的。让我们测试一下:

    type Test = Mapped<[1, 2, 3]>;
    // type Test = readonly [number, number, number]
    

    看起来不错。


    如您所见,编译器将无法验证squareTuple() 的实现是否满足调用签名的返回类型。 TS standard library's typings for Array.prototype.map() 指定它返回一个数组,而不是一个元组。有些type assertions是按顺序排列的,比如:

    function squareTuple<NumList extends ReadonlyArray<number>>(nums: NumList): Mapped<NumList> {
      return nums.map(square) as readonly number[] as Mapped<NumList>;
    }
    

    在此之后一切都应该工作:

    const [literalValue] = [9, 16, 25] as const;
    const [value] = squareTuple([3, 4, 5] as const);
    const [spreadValue] = squareSpreadTuple(3, 4, 5);
    console.log(`Sum of values is: ${literalValue + value + spreadValue}`); // okay
    

    Playground link to code

    【讨论】:

      【解决方案2】:

      我认为您不需要映射元组类型。强制转换为 NumList 是必要的,Array.map() 可以返回一个空数组,但我们知道它不是空的。

      function squareTuple<NumList extends readonly [...number[]]>(nums:NumList): NumList {
        return nums.map(square) as unknown as NumList;
      }
      
      const [constValue] = squareTuple([3,4,5] as const); 
      // const normalValue: 3, doesn't matter
      const [normalValue] = squareTuple([3,4,5]); 
      // const normalValue: number
      
      const [emptyConstValue] = squareTuple([] as const); 
      // const emptyConstValue: undefined 
      // error: Tuple type 'readonly []' of length '0' has no element at index '0'
      const [emptyValue] = squareTuple([]); 
      // const emptyValue: undefined
      // error: Tuple type 'readonly []' of length '0' has no element at index '0'
      
      const [literalValue] = [3,4,5];
      console.log(`Sum of values is: ${literalValue + normalValue }`)
      // "Sum of values is: 12"
      console.log(`Sum of values is: ${literalValue + emptyValue}`)
      // error: Object is possibly 'undefined'.(2532)
      

      Playground

      【讨论】:

        猜你喜欢
        • 2012-03-09
        • 2015-12-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-09
        • 1970-01-01
        • 1970-01-01
        • 2020-11-18
        相关资源
        最近更新 更多