Búsqueda de sitios web

typescript - error 'no se puede asignar al parámetro de tipo' con literal de objeto


Hay un problema con el tipo literal de objeto.

interface OptionalFoo {
  foo?: number;
}

interface Bar {}

function foobarFn(foobar: OptionalFoo & Bar) {}

foobarFn({ bar: 1 }); // error

foobarFn({ bar: 1 } as { bar: number }); // ok

foobarFn({ bar: 1 } as { bar: 1 }); // ok!

Literal de objeto con tipo inferido provoca error de tipo:

El argumento de tipo '{ bar: number; }' no es asignable al parámetro de tipo 'OptionalFoo & Bar'

Pero el problema no es la inferencia en sí:

const bar = { bar: 1 }; // inferred { bar: number; }
foobarFn(bar); // ok!?

El mismo problema con la sintaxis spread frente a Object.assign:

foobarFn({...{ bar: 1 }}); // error

foobarFn(Object.assign({}, { bar: 1 })); // ok!?

¿Hay alguna manera de lograr el comportamiento con tipo literal de objeto inferido (verificación de propiedades excedentes) sin un literal de objeto en el lugar, por ejemplo, con la variable bar o una llamada a función como Object.assign({ bar: 1 })?

Respuesta #1

Solo para dejar las cosas claras, esto no se trata solo de las verificaciones de propiedades excedentes. Las verificaciones de propiedades excedentes entran en juego cuando asignamos un literal de objeto directamente a una ubicación. En tu caso, todo el comportamiento más sorprendente ocurre al asignar un objeto de manera indirecta, lo cual normalmente estaría permitido bajo las verificaciones de propiedades excedentes.

function foo(o: { bar: number }) { }
foo({ bar: 0, foo: "" }) // error direct assignment
foo({ bar:0, foo: ""} as { bar:0, foo: "" }) // ok indirect

La parte sorprendente, al menos para mí, es que otro control (el control de tipo débil) no detecta este error. Bajo controles de tipo débiles (como se describe aquí) si un tipo tiene solo propiedades opcionales, y tratamos de asignar un tipo que no tiene propiedades en común con él, deberíamos obtener un error:

function foo(o: { bar?: number }) { }
foo({ foo: "" }) // error under excess properties:  Object literal may only specify known properties, and 'foo' does not exist in type
foo({ foo: ""} as { foo: "" }) // error under weak types: Type '{ foo: ""; }' has no properties in common with type '{ bar?: number; }'.

Creo que esto es un agujero (dudo en decir que es un error, no estoy seguro si es intencional o no) en las verificaciones de tipo débiles. Un tipo débil es (según este PR):

  1. Tipos de objetos con al menos una propiedad
  2. Donde todas las propiedades son opcionales
  3. Y que no tenga una firma de índice de cadena, firma de índice de número, firma de llamada o firma de construcción.

Sin embargo, en la implementación de la verificación de tipo débil para intersecciones, todos los tipos de la intersección deben ser tipos débiles para que la intersección sea un tipo débil. Del código del compilador (comentarios añadidos):

function isWeakType(type: Type): boolean {
    if (type.flags & TypeFlags.Object) {
       // ....
    }
    if (type.flags & TypeFlags.Intersection) {
        /// All intersection members have to be weak
        return every((<IntersectionType>type).types, isWeakType); 
    }
    return false;
}

Dado que interface Bar {} no es un tipo débil (según la primera regla, no tiene propiedades), cualquier intersección con él no será un tipo débil y no generará ningún error de verificación de tipo débil. Eliminar Bar de la intersección generará errores en todas partes donde asignes un objeto que no tenga nada en común con el objetivo.