typescript - ¿Cómo establezco los valores iniciales de un Map tipado en el constructor cuando las claves tienen múltiples tipos?
Quiero crear un Map donde la clave pueda ser un number o un string. Quiero inicializarlo con valores en el constructor. Algunas entradas tienen una clave number y otras tienen una clave string.
Pero recibo un error de TypeScript. ¿Es esto un error de TypeScript o estoy haciendo algo mal?
type MyMap = Map<string | number, string>;
// Errors, incorrectly?
const myMapWithMixedKeys: MyMap = new Map([
['abc', 'String key'],
[123, 'Number key']
]);
// All fine
myMapWithMixedKeys.set('abcd', 'String key');
myMapWithMixedKeys.set(1234, 'Number key');
const myMapStrings: MyMap = new Map([
['abc', 'String key'],
['abcd', 'String key']
]);
const myMapNumbers: MyMap = new Map([
[123, 'Number key'],
[1234, 'Number key']
]);
El error:
No overload matches this call.
Overload 1 of 3, '(iterable: Iterable<readonly [string, string]>): Map<string, string>', gave the following error.
Argument of type '([string, string] | [number, string])[]' is not assignable to parameter of type 'Iterable<readonly [string, string]>'.
Types of property '[Symbol.iterator]' are incompatible.
Type '() => IterableIterator<[string, string] | [number, string]>' is not assignable to type '() => Iterator<readonly [string, string], any, undefined>'.
Type 'IterableIterator<[string, string] | [number, string]>' is not assignable to type 'Iterator<readonly [string, string], any, undefined>'.
Types of property 'next' are incompatible.
Type '(...args: [] | [undefined]) => IteratorResult<[string, string] | [number, string], any>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult<readonly [string, string], any>'.
Type 'IteratorResult<[string, string] | [number, string], any>' is not assignable to type 'IteratorResult<readonly [string, string], any>'.
Type 'IteratorYieldResult<[string, string] | [number, string]>' is not assignable to type 'IteratorResult<readonly [string, string], any>'.
Type 'IteratorYieldResult<[string, string] | [number, string]>' is not assignable to type 'IteratorYieldResult<readonly [string, string]>'.
Type '[string, string] | [number, string]' is not assignable to type 'readonly [string, string]'.
Type '[number, string]' is not assignable to type 'readonly [string, string]'.
Types of property '0' are incompatible.
Type 'number' is not assignable to type 'string'.
Overload 2 of 3, '(entries?: readonly (readonly [string, string])[] | null | undefined): Map<string, string>', gave the following error.
Argument of type '([string, string] | [number, string])[]' is not assignable to parameter of type 'readonly (readonly [string, string])[]'.
Type '[string, string] | [number, string]' is not assignable to type 'readonly [string, string]'.
Type '[number, string]' is not assignable to type 'readonly [string, string]'.ts(2769)
Respuesta #1
El problema es que TS infiere el tipo de un new Map() por su primer valor. Sucede incluso si escribes solo let m=new Map([[1, 1], ['2', 2]]), sin ningún tipo definido.
(PD: También sucede en los arreglos cuando escribes algo como let a=new Array(1, '2')).
Para resolver esto, deberías forzar a TS a entender que el tipo del Map es un tipo unión. Conozco dos formas de hacer esto:
const myMapWithMixedKeys: MyMap = new Map<string | number, string>([
['abc', 'String key'],
[123, 'Number key']
]);
O :
const myMapWithMixedKeys: MyMap = new Map([
['abc' as string | number, 'String key'],
[123, 'Number key']
]);
Ver patio de recreo
EDITAR: Encontré una mejor solución, en mi opinión.
En lugar de:
type MyMap = Map<string | number, string>;
Escribe :
class MyMap extends Map<string | number, string> { }
Y ahora todo funciona bien. Ve el Playground actualizado
