一个函数 return 可以是一个没有 noUncheckedIndexedAccess 错误的映射元组吗?
Can a function return a mapped tuple without noUncheckedIndexedAccess errors?
下面的代码示例无法使用 noUncheckedIndexedAccess 进行编译。虽然已知 literalValue
是一个数字,但 value
和 spreadValue
变量在其类型联合中具有 'undefined'。
有什么方法可以注释 squareTuple 和 squareSpreadTuple 函数,以便传递一个特定元数的元组将 return 一个可以像文字元组一样被解构的结果,而不会出现虚假的未定义类型联合?
示例代码中的 Mapped 类型尝试这样做(通过将元组的长度属性传递给它的 return)。好像没有效果。
是否还有其他方法可以以解构会尊重的方式将参数元组的元数转移到它的 returns?
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 中更简单的方法就可以工作。不过,我发现很难解释为什么非传播案例无法使用这种更简单的方法进行编译。
我倾向于这样写Mapped
:
type Mapped<L extends readonly number[]> = { readonly [K in keyof L]: number }
这是使用 TypeScript 对 using mapped types to turn arrays/tuples into arrays/tuples. Such mappings already preserve the length
property without you having to do anything in particular. Presumably all you're trying to do with Mapped
is to undo any numeric literal type 推理的支持,并扩大回 number
。这就是上面的 Mapped
所做的。让我们来测试一下:
type Test = Mapped<[1, 2, 3]>;
// type Test = readonly [number, number, number]
看起来不错。
如您所见,编译器将无法验证 squareTuple()
的实现是否满足调用签名的 return 类型。 TS standard library's typings for Array.prototype.map()
specify that it returns an array, not a tuple. Some 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
我认为您不需要映射元组类型。虽然转换为 NumList 是必要的,Array.map() 可以 return 一个空数组,但我们知道它不是空的。
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)
下面的代码示例无法使用 noUncheckedIndexedAccess 进行编译。虽然已知 literalValue
是一个数字,但 value
和 spreadValue
变量在其类型联合中具有 'undefined'。
有什么方法可以注释 squareTuple 和 squareSpreadTuple 函数,以便传递一个特定元数的元组将 return 一个可以像文字元组一样被解构的结果,而不会出现虚假的未定义类型联合?
示例代码中的 Mapped 类型尝试这样做(通过将元组的长度属性传递给它的 return)。好像没有效果。
是否还有其他方法可以以解构会尊重的方式将参数元组的元数转移到它的 returns?
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 中更简单的方法就可以工作。不过,我发现很难解释为什么非传播案例无法使用这种更简单的方法进行编译。
我倾向于这样写Mapped
:
type Mapped<L extends readonly number[]> = { readonly [K in keyof L]: number }
这是使用 TypeScript 对 using mapped types to turn arrays/tuples into arrays/tuples. Such mappings already preserve the length
property without you having to do anything in particular. Presumably all you're trying to do with Mapped
is to undo any numeric literal type 推理的支持,并扩大回 number
。这就是上面的 Mapped
所做的。让我们来测试一下:
type Test = Mapped<[1, 2, 3]>;
// type Test = readonly [number, number, number]
看起来不错。
如您所见,编译器将无法验证 squareTuple()
的实现是否满足调用签名的 return 类型。 TS standard library's typings for Array.prototype.map()
specify that it returns an array, not a tuple. Some 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
我认为您不需要映射元组类型。虽然转换为 NumList 是必要的,Array.map() 可以 return 一个空数组,但我们知道它不是空的。
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)