纠正和统一解构列表的类型
Correct and unify type of destructured list
我想操作值数组的数组,但在解构列表时找不到正确的类型。
我数组的值不是 . I am neither looking into . I am trying to destructure arrays, .
我有以下 ts 的非编译代码片段:
const bar = (n: number, s: string, p: string): string => n === 1 ? s : p;
const f = (args: [number, string, string]): string => bar(...args)
const r = [
[1, 'a', 'b'],
[2, 'c', 'd'],
].map(f);
抛出错误:
Argument of type '(args: [number, string, string]) => string' is not assignable to parameter of type '(value: (string | number)[], index: number, array: (string | number)[][]) => string'.
Types of parameters 'args' and 'value' are incompatible.
Type '(string | number)[]' is not assignable to type '[number, string, string]'.
Target requires 3 element(s) but source may have fewer.
与 Ts v4.3.5 offiial editor。生成的 js 确实适用于 node
➜ ~ node
Welcome to Node.js v14.17.0.
Type ".help" for more information.
> const bar = (n, s, p) => n === 1 ? s : p;
undefined
> const f = (args) => bar(...args);
undefined
> const r = [
... [1, 'a', 'b'],
... [2, 'c', 'd'],
... ].map(f);
undefined
> r
[ 'a', 'd' ]
是否可以概括这一点?
背景
不幸的是,问题是 TypeScript 不会为您期望它推断的输入数据推断元组类型。
考虑以下类型:
const t = [1, 'a', 'b']
// type of t: (string | number)[]
const t2 = [1, 'a', 'b'] as const
// type of t2: readonly [1, 'a', 'b']
第一个赋值将推断给定元素类型的联合数组。在第二个赋值中,我们使用 as const
加法来强制编译器推断最具体的类型。如您所见,它现在实际上推断文字类型(例如 'a' 而不是字符串)。
这可能也不总是我们想要的。您当然可以将元素转换为正确的类型。然而,我自己经常使用一个辅助函数,它除了推断我最常需要的类型外什么都不做:
function tuple<T extends unknown[]>(...tup: T): T {
return tup;
}
const t3 = tuple(1, 'a', 'b')
// type of t3: [number, string, string]
解决方案 1
让我们将这些知识应用到您的示例中:
const input = [
[1, 'a', 'b'],
[2, 'c', 'd'],
]
// type of input: (string | number)[][]
const input2 = [
tuple(1, 'a', 'b'),
tuple(2, 'c', 'd')
]
// type of input2: [number, string, string][]
最后一个类型正是您输入类型 f
所需要的。
结合所有内容,我们得到:
const bar = (n: number, s: string, p: string): string => n === 1 ? s : p;
const f = (args: [number, string, string]): string => bar(...args)
function tuple<T extends unknown[]>(...tup: T): T {
return tup;
}
const r = [
tuple(1, 'a', 'b'),
tuple(2, 'c', 'd'),
].map(f);
// type of r: string[]
解决方案 2
如果您不想细化输入类型,您还可以为 .map
函数创建一个帮助程序来推断正确的类型:
const bar = (n: number, s: string, p: string): string => (n === 1 ? s : p);
const f = (args: [number, string, string]): string => bar(...args);
function mapValues<T, R>(values: T[], f: (value: T) => R): R[] {
return values.map(f);
}
const r = mapValues(
[
[1, "a", "b"],
[2, "c", "d"]
],
f
);
结果应该是一样的。
解决方案 3
如果您真的不喜欢或不能使用辅助函数,我能想到的最简单的解决方案是使用 as const
。您需要稍微更改 f
的类型,因为数组将变为只读:
const bar = (n: number, s: string, p: string): string => (n === 1 ? s : p);
const f = (args: readonly [number, string, string]): string => bar(...args);
const r = ([
[1, "a", "b"],
[2, "c", "d"]
] as const).map(f);
您 f
函数期望它的第一个参数是一个元组:[number, string, string]
.
但是,数组 r
中的项目的类型为 (string | number)[]
。所以一个字符串或数字数组。
因此,您需要告诉 TypeScript 数组项是元组。例如:
const r: [number, string, string][] = [
[1, 'a', 'b'],
[2, 'c', 'd'],
];
const result = r.map(f);
我想操作值数组的数组,但在解构列表时找不到正确的类型。
我数组的值不是
我有以下 ts 的非编译代码片段:
const bar = (n: number, s: string, p: string): string => n === 1 ? s : p;
const f = (args: [number, string, string]): string => bar(...args)
const r = [
[1, 'a', 'b'],
[2, 'c', 'd'],
].map(f);
抛出错误:
Argument of type '(args: [number, string, string]) => string' is not assignable to parameter of type '(value: (string | number)[], index: number, array: (string | number)[][]) => string'.
Types of parameters 'args' and 'value' are incompatible.
Type '(string | number)[]' is not assignable to type '[number, string, string]'.
Target requires 3 element(s) but source may have fewer.
与 Ts v4.3.5 offiial editor。生成的 js 确实适用于 node
➜ ~ node
Welcome to Node.js v14.17.0.
Type ".help" for more information.
> const bar = (n, s, p) => n === 1 ? s : p;
undefined
> const f = (args) => bar(...args);
undefined
> const r = [
... [1, 'a', 'b'],
... [2, 'c', 'd'],
... ].map(f);
undefined
> r
[ 'a', 'd' ]
是否可以概括这一点?
背景
不幸的是,问题是 TypeScript 不会为您期望它推断的输入数据推断元组类型。
考虑以下类型:
const t = [1, 'a', 'b']
// type of t: (string | number)[]
const t2 = [1, 'a', 'b'] as const
// type of t2: readonly [1, 'a', 'b']
第一个赋值将推断给定元素类型的联合数组。在第二个赋值中,我们使用 as const
加法来强制编译器推断最具体的类型。如您所见,它现在实际上推断文字类型(例如 'a' 而不是字符串)。
这可能也不总是我们想要的。您当然可以将元素转换为正确的类型。然而,我自己经常使用一个辅助函数,它除了推断我最常需要的类型外什么都不做:
function tuple<T extends unknown[]>(...tup: T): T {
return tup;
}
const t3 = tuple(1, 'a', 'b')
// type of t3: [number, string, string]
解决方案 1
让我们将这些知识应用到您的示例中:
const input = [
[1, 'a', 'b'],
[2, 'c', 'd'],
]
// type of input: (string | number)[][]
const input2 = [
tuple(1, 'a', 'b'),
tuple(2, 'c', 'd')
]
// type of input2: [number, string, string][]
最后一个类型正是您输入类型 f
所需要的。
结合所有内容,我们得到:
const bar = (n: number, s: string, p: string): string => n === 1 ? s : p;
const f = (args: [number, string, string]): string => bar(...args)
function tuple<T extends unknown[]>(...tup: T): T {
return tup;
}
const r = [
tuple(1, 'a', 'b'),
tuple(2, 'c', 'd'),
].map(f);
// type of r: string[]
解决方案 2
如果您不想细化输入类型,您还可以为 .map
函数创建一个帮助程序来推断正确的类型:
const bar = (n: number, s: string, p: string): string => (n === 1 ? s : p);
const f = (args: [number, string, string]): string => bar(...args);
function mapValues<T, R>(values: T[], f: (value: T) => R): R[] {
return values.map(f);
}
const r = mapValues(
[
[1, "a", "b"],
[2, "c", "d"]
],
f
);
结果应该是一样的。
解决方案 3
如果您真的不喜欢或不能使用辅助函数,我能想到的最简单的解决方案是使用 as const
。您需要稍微更改 f
的类型,因为数组将变为只读:
const bar = (n: number, s: string, p: string): string => (n === 1 ? s : p);
const f = (args: readonly [number, string, string]): string => bar(...args);
const r = ([
[1, "a", "b"],
[2, "c", "d"]
] as const).map(f);
您 f
函数期望它的第一个参数是一个元组:[number, string, string]
.
但是,数组 r
中的项目的类型为 (string | number)[]
。所以一个字符串或数字数组。
因此,您需要告诉 TypeScript 数组项是元组。例如:
const r: [number, string, string][] = [
[1, 'a', 'b'],
[2, 'c', 'd'],
];
const result = r.map(f);