new Map([[1, [2, 3]] ].map(e => e[1]));
是完全有效的 javascript。但是打字稿给出了以下错误:
No overload matches this call.
Overload 1 of 3, '(iterable: Iterable<readonly [unknown, unknown]>): Map<unknown, unknown>', gave the following error.
Argument of type '(number | number[])[]' is not assignable to parameter of type 'Iterable<readonly [unknown, unknown]>'.
The types returned by '[Symbol.iterator]().next(...)' are incompatible between these types.
Type 'IteratorResult<number | number[], any>' is not assignable to type 'IteratorResult<readonly [unknown, unknown], any>'.
Type 'IteratorYieldResult<number | number[]>' is not assignable to type 'IteratorResult<readonly [unknown, unknown], any>'.
Type 'IteratorYieldResult<number | number[]>' is not assignable to type 'IteratorYieldResult<readonly [unknown, unknown]>'.
Type 'number | number[]' is not assignable to type 'readonly [unknown, unknown]'.
Type 'number' is not assignable to type 'readonly [unknown, unknown]'.
Overload 2 of 3, '(entries?: readonly (readonly [unknown, unknown])[] | null | undefined): Map<unknown, unknown>', gave the following error.
Argument of type '(number | number[])[]' is not assignable to parameter of type 'readonly (readonly [unknown, unknown])[]'.
Type 'number | number[]' is not assignable to type 'readonly [unknown, unknown]'.
Type 'number' is not assignable to type 'readonly [unknown, unknown]'.
似乎编译器将 Map
的签名与 [[1, [2, 3]]
匹配而不是转换后的 [2, 3]
。
如果 Map
只使用只读数组的后续操作:
new Map([[2, 3]]);
是正确的?new Map(Object.freeze([[1, [2, 3]]].map(e => e[1])));
返回 Object.freeze
数组,为什么 readonly
不起作用?TS 编译器理论上应该能够推导出所有三种形式的等价性。
答案 0 :(得分:1)
让我们深入研究 MapConstructor
的类型定义
interface MapConstructor {
new(): Map<any, any>;
new<K, V>(entries?: readonly (readonly [K, V])[] | null): Map<K, V>;
readonly prototype: Map<any, any>;
}
根据类型,如果你想提供一个数组作为参数 - 在我们的例子中 entries
,这个数组应该是不可变的。
因此,为了使其工作,只需添加 as const
。
const arr = [[1, [2, 3]]] as const
const x = new Map(arr); // ok
或
const x = new Map( ([[1, [2, 3]]] as const).map(e => e[1])); // ok
<块引用>
新地图([[2, 3]]);正确吗?
看到这个类型定义:
interface MapConstructor {
new(): Map<any, any>;
/* -----> */ new<K, V>(entries?: readonly (readonly [K, V])[] | null): Map<K, V>;
readonly prototype: Map<any, any>;
}
在这里,您处理了泛型,因此 TS 能够推断出参数,即您有一个正好包含两个元素(键和值)的数组。这是最重要的事情。因为这是 Map 的性质,所以你应该有 key 和 value。
请参阅 excess property checks 和下一个示例:
const infer = <K, V>(arg: readonly [K, V]) => arg
const x = infer([1, 2])
如您所见,因为您有文字数组,所以 TS 能够确定您正好有两个值:键和值。
但是,如果您使用引用作为参数(请再次查看多余的属性检查):
const infer = <K, V>(arg: readonly [K, V]) => arg
const tuple = [1,2] // number[]
const x = infer(tuple)
TS 会抱怨,baceuse tuple
被推断为 number[]
并且 TS 无法确定您的数组中有多少个元素。它甚至可以是空数组。这就是为什么 TS 不允许您在此处使用对可变数组的引用。
为什么 new Map(Object.freeze([[1, [2, 3]]]].map(e => e1))) 不起作用?
两者之间存在很大差异:
Object.freeze([[1, [2, 3]]].map(e => e[1]))
和
([[1, [2, 3]]] as const).map(e => e[1])
在第一种情况下,您可以改变 map
谓词内的数组,但无法改变结果。
在第二种情况下,您无法改变 map
中的数组。
请查看它们的类型签名:
type First = readonly (number | number[])[]
type Second = (readonly [2, 3])[]
let x: First = [1] // is allowed but invalid argument
let y: Second = [1] // does not allowed, because we should have a kay and value. not only key