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
isToDoDtoes 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 unIToDoDtoen este puntoasanyno está garantizado que lo estésIToDoDtotodaví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.tsarchivos de interfaz en esquemas JSON y usarlosajvpara 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
afaltaban propiedades que existíanb. En otras palabras,atení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
bque no está implementado en el tipoa, debería darse cuenta de una falla en tiempo de ejecución.fuente
getysetmétodos.