Estou tentando fazer uma conversão em meu código do corpo de uma solicitação em expresso (usando middleware analisador de corpo) para uma interface, mas não está impondo a segurança de tipo.
Esta é minha interface:
export interface IToDoDto {
description: string;
status: boolean;
};
Este é o código onde estou tentando fazer o elenco:
@Post()
addToDo(@Response() res, @Request() req) {
const toDo: IToDoDto = <IToDoDto> req.body; // <<< cast here
this.toDoService.addToDo(toDo);
return res.status(HttpStatus.CREATED).end();
}
E, finalmente, o método de serviço que está sendo chamado:
public addToDo(toDo: IToDoDto): void {
toDo.id = this.idCounter;
this.todos.push(toDo);
this.idCounter++;
}
Posso passar quaisquer argumentos, mesmo aqueles que não chegam perto de corresponder à definição da interface , e este código funcionará bem. Eu esperaria, se a conversão do corpo da resposta para a interface não fosse possível, que uma exceção fosse lançada no tempo de execução como Java ou C #.
Eu li que na conversão de TypeScript não existe, apenas Type Assertion, então isso só dirá ao compilador que um objeto é do tipo x
, então ... Estou errado? Qual é a maneira certa de aplicar e garantir a segurança de tipo?
fonte
Respostas:
Não há transmissão em javascript, então você não pode jogar se a "transmissão falhar".
O texto digitado oferece suporte à conversão, mas isso é apenas para o tempo de compilação, e você pode fazer assim:
const toDo = <IToDoDto> req.body; // or const toDo = req.body as IToDoDto;
Você pode verificar em tempo de execução se o valor é válido e se não lançar um erro, ou seja:
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 @huyz apontou, não há necessidade de asserção de tipo porque
isToDoDto
é uma proteção de tipo, então isso deve ser suficiente:if (!isToDoDto(req.body)) { throw new Error("invalid request"); } this.toDoService.addToDo(req.body);
fonte
const toDo = req.body as IToDoDto;
já que o compilador TS sabe que é umIToDoDto
neste pontoas
any
ainda não tem garantia de serIToDoDto
!", Mas TS escolheu não fazer. Se o compilador detecta apenas alguns conflitos de tipo, e nenhum no código transpilado (e você está certo; eu deveria ter sido mais claro @ do que no original), as interfaces são, infelizmente, imo, [principalmente?] Açucaradas.Esta é outra maneira de forçar uma conversão de tipo, mesmo entre tipos e interfaces incompatíveis onde o compilador TS normalmente reclama:
export function forceCast<T>(input: any): T { // ... do runtime checks here // @ts-ignore <-- forces TS compiler to compile this as-is return input; }
Em seguida, você pode usá-lo para forçar a projeção de objetos para um determinado tipo:
import { forceCast } from './forceCast'; const randomObject: any = {}; const typedObject = forceCast<IToDoDto>(randomObject);
Observe que deixei de fora a parte que você deve fazer as verificações de tempo de execução antes do lançamento, para reduzir a complexidade. O que faço em meu projeto é compilar todos os meus
.d.ts
arquivos de interface em esquemas JSON e usáajv
-los para validar em tempo de execução.fonte
Se isso ajudar alguém, eu estava tendo um problema em que queria tratar um objeto como outro tipo com uma interface semelhante. Tentei o seguinte:
Não passou fiapos
const x = new Obj(a as b);
O linter reclamava que
a
faltavam propriedades que existiam emb
. Em outras palavras,a
tinha algumas propriedades e métodos deb
, mas não todos. Para contornar isso, segui a sugestão do VS Code:Teste e lint aprovado
const x = new Obj(a as unknown as b);
Observe que se o seu código tentar chamar uma das propriedades que existe no tipo
b
que não está implementado no tipoa
, você deve perceber uma falha de tempo de execução.fonte
get
e JavaScriptset
.