Búsqueda de sitios web

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

Artículos relacionados