Estoy tratando de hacer una conversión en mi código desde el cuerpo de una solicitud en express (usando middleware body-parser) a una interfaz, pero no aplica la seguridad de tipos.
Esta es mi interfaz:
export interface IToDoDto {
description: string;
status: boolean;
};
Este es el código en el que intento hacer el reparto:
@Post()
addToDo(@Response() res, @Request() req) {
const toDo: IToDoDto = <IToDoDto> req.body; // <<< cast here
this.toDoService.addToDo(toDo);
return res.status(HttpStatus.CREATED).end();
}
Y finalmente, el método de servicio que se llama:
public addToDo(toDo: IToDoDto): void {
toDo.id = this.idCounter;
this.todos.push(toDo);
this.idCounter++;
}
Puedo pasar cualquier argumento, incluso los que no se acercan a la definición de la interfaz , y este código funcionará bien. Esperaría que, si la conversión del cuerpo de respuesta a la interfaz no es posible, se lance una excepción en tiempo de ejecución como Java o C #.
He leído que en TypeScript la conversión no existe, solo Type Assertion, por lo que solo le dirá al compilador que un objeto es de tipo x
, así que ... ¿Me equivoco? ¿Cuál es la forma correcta de hacer cumplir y garantizar la seguridad de tipos?
fuente
Respuestas:
No hay conversión en javascript, por lo que no puede lanzar si "falla".
TypeScript admite la transmisión, pero eso es solo para el tiempo de compilación, y puede hacerlo así:
const toDo = <IToDoDto> req.body; // or const toDo = req.body as IToDoDto;
Puede verificar en tiempo de ejecución si el valor es válido y si no arroja un error, es decir:
function isToDoDto(obj: any): obj is IToDoDto { return typeof obj.description === "string" && typeof obj.status === "boolean"; } @Post() addToDo(@Response() res, @Request() req) { if (!isToDoDto(req.body)) { throw new Error("invalid request"); } const toDo = req.body as IToDoDto; this.toDoService.addToDo(toDo); return res.status(HttpStatus.CREATED).end(); }
Editar
Como señaló @huyz, no hay necesidad de la aserción de tipo porque
isToDoDto
es una protección de tipo, por lo que esto debería ser suficiente:if (!isToDoDto(req.body)) { throw new Error("invalid request"); } this.toDoService.addToDo(req.body);
fuente
const toDo = req.body as IToDoDto;
ya que el compilador de TS sabe que es unIToDoDto
en este puntoas
any
no está garantizado que lo estésIToDoDto
todavía!", Pero TS decidió no hacerlo. Si el compilador solo detecta algunos conflictos de tipos, y ninguno en el código transpilado (y tienes razón; debería haber sido más claro @ que en el original), las interfaces son desafortunadamente, en mi opinión, [¿en su mayoría?] Azúcar.Aquí hay otra forma de forzar una conversión de tipos incluso entre tipos e interfaces incompatibles donde el compilador TS normalmente se queja:
export function forceCast<T>(input: any): T { // ... do runtime checks here // @ts-ignore <-- forces TS compiler to compile this as-is return input; }
Luego, puede usarlo para forzar a lanzar objetos a un cierto tipo:
import { forceCast } from './forceCast'; const randomObject: any = {}; const typedObject = forceCast<IToDoDto>(randomObject);
Tenga en cuenta que omití la parte en la que se supone que debe hacer comprobaciones en tiempo de ejecución antes de lanzar para reducir la complejidad. Lo que hago en mi proyecto es compilar todos mis
.d.ts
archivos de interfaz en esquemas JSON y usarlosajv
para validar en tiempo de ejecución.fuente
Si ayuda a alguien, estaba teniendo un problema en el que quería tratar un objeto como otro tipo con una interfaz similar. Intenté lo siguiente:
No pasó pelusa
const x = new Obj(a as b);
El linter se quejaba de que
a
faltaban propiedades que existíanb
. En otras palabras,a
tenía algunas propiedades y métodosb
, pero no todos. Para solucionar esto, seguí la sugerencia de VS Code:Pasó el deshilachado y las pruebas.
const x = new Obj(a as unknown as b);
Tenga en cuenta que si su código intenta llamar a una de las propiedades que existen en el tipo
b
que no está implementado en el tipoa
, debería darse cuenta de una falla en tiempo de ejecución.fuente
get
yset
métodos.