Próbuję wykonać rzutowanie w moim kodzie z treści żądania w trybie ekspresowym (przy użyciu oprogramowania pośredniczącego z parserem treści) do interfejsu, ale nie wymusza to bezpieczeństwa typów.
To jest mój interfejs:
export interface IToDoDto {
description: string;
status: boolean;
};
Oto kod, w którym próbuję wykonać rzut:
@Post()
addToDo(@Response() res, @Request() req) {
const toDo: IToDoDto = <IToDoDto> req.body; // <<< cast here
this.toDoService.addToDo(toDo);
return res.status(HttpStatus.CREATED).end();
}
I wreszcie wywoływana metoda usługi:
public addToDo(toDo: IToDoDto): void {
toDo.id = this.idCounter;
this.todos.push(toDo);
this.idCounter++;
}
Mogę przekazać dowolne argumenty, nawet takie, które nie są bliskie dopasowania definicji interfejsu , a ten kod będzie działał dobrze. Spodziewałbym się, że jeśli rzutowanie z treści odpowiedzi na interfejs nie jest możliwe, wyjątek zostanie wyrzucony w czasie wykonywania, jak Java lub C #.
Wyczytałem, że w rzutowaniu TypeScript nie istnieje, tylko Asercja typu, więc tylko powie kompilatorowi, że obiekt jest typu x
, więc ... Czy się mylę? Jaki jest właściwy sposób egzekwowania i zapewnienia bezpieczeństwa typu?
źródło
Odpowiedzi:
W javascript nie ma przesyłania, więc nie możesz go przesłać, jeśli „rzutowanie się nie powiedzie”.
Typescript obsługuje rzutowanie, ale to tylko na czas kompilacji i możesz to zrobić w następujący sposób:
const toDo = <IToDoDto> req.body; // or const toDo = req.body as IToDoDto;
Możesz sprawdzić w czasie wykonywania, czy wartość jest prawidłowa, a jeśli nie, zgłoś błąd, np .:
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(); }
Edytować
Jak zauważył @huyz, asercja typu nie jest potrzebna, ponieważ
isToDoDto
jest to strażnik typu, więc to powinno wystarczyć:if (!isToDoDto(req.body)) { throw new Error("invalid request"); } this.toDoService.addToDo(req.body);
źródło
const toDo = req.body as IToDoDto;
ponieważ kompilator TS wie, że jestIToDoDto
w tym momencieas
any
gwarancjiIToDoDto
!”, Ale TS zdecydował się tego nie robić . Jeśli kompilator wychwytuje tylko niektóre konflikty typów, a żadnego w transpiled kodu (i masz rację; powinienem był być bardziej jasny @ niż w oryginale), interfejsy są niestety, imo, [głównie?] Sugar.Oto inny sposób wymuszenia rzutowania typu nawet między niekompatybilnymi typami i interfejsami, w przypadku których kompilator TS normalnie narzeka:
export function forceCast<T>(input: any): T { // ... do runtime checks here // @ts-ignore <-- forces TS compiler to compile this as-is return input; }
Następnie możesz go użyć do wymuszenia rzucania obiektów do określonego typu:
import { forceCast } from './forceCast'; const randomObject: any = {}; const typedObject = forceCast<IToDoDto>(randomObject);
Zauważ, że pominąłem część, którą powinieneś sprawdzić w czasie wykonywania przed rzutowaniem, aby zmniejszyć złożoność. To, co robię w moim projekcie, to kompilowanie wszystkich
.d.ts
plików interfejsu do schematów JSON i używanie ichajv
do sprawdzania poprawności w czasie wykonywania.źródło
Jeśli komuś to pomoże, miałem problem polegający na tym, że chciałem traktować obiekt jako inny typ z podobnym interfejsem. Próbowałem wykonać następujące czynności:
Nie przeszedł lintingu
const x = new Obj(a as b);
Linter narzekał, że
a
brakuje mu właściwości, które istniałyb
. Innymi słowy,a
miał pewne właściwości i metodyb
, ale nie wszystkie. Aby obejść ten problem, postąpiłem zgodnie z sugestią VS Code:Przeszedł linting i testy
const x = new Obj(a as unknown as b);
Należy zauważyć, że jeśli kod próbuje wywołać jedną z właściwości, które istnieją w typie,
b
który nie jest zaimplementowany w typiea
, należy zrealizować błąd czasu wykonywania.źródło
get
iset
metod.