Я пытаюсь выполнить приведение своего кода из тела запроса в экспресс (с использованием промежуточного программного обеспечения парсера тела) в интерфейс, но это не обеспечивает безопасность типов.
Это мой интерфейс:
export interface IToDoDto {
description: string;
status: boolean;
};
Это код, в котором я пытаюсь выполнить приведение:
@Post()
addToDo(@Response() res, @Request() req) {
const toDo: IToDoDto = <IToDoDto> req.body; // <<< cast here
this.toDoService.addToDo(toDo);
return res.status(HttpStatus.CREATED).end();
}
И, наконец, вызываемый метод сервиса:
public addToDo(toDo: IToDoDto): void {
toDo.id = this.idCounter;
this.todos.push(toDo);
this.idCounter++;
}
Я могу передавать любые аргументы, даже те, которые не подходят для соответствия определению интерфейса , и этот код будет работать нормально. Я ожидал, что если приведение тела ответа к интерфейсу невозможно, то во время выполнения, например Java или C #, будет выброшено исключение.
Я читал, что в TypeScript не существует приведения типов, только Type Assertion, поэтому он будет сообщать компилятору только о том, что объект имеет тип x
, так что ... Я ошибаюсь? Как правильно применять и обеспечивать безопасность типов?
источник
Ответы:
В javascript нет приведения, поэтому вы не можете выбросить, если «приведение не удалось».
TypeScript поддерживает приведение типов, но это только на время компиляции, и вы можете сделать это следующим образом:
const toDo = <IToDoDto> req.body; // or const toDo = req.body as IToDoDto;
Вы можете проверить во время выполнения, является ли значение допустимым, а если нет, вывести ошибку, то есть:
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(); }
редактировать
Как отметил @huyz, утверждение типа не нужно, потому что
isToDoDto
это защита типа, поэтому этого должно быть достаточно:if (!isToDoDto(req.body)) { throw new Error("invalid request"); } this.toDoService.addToDo(req.body);
источник
const toDo = req.body as IToDoDto;
так как компилятор TS знает, что этоIToDoDto
на данный моментas
any
это еще не гарантированоIToDoDto
!», Но TS предпочла этого не делать. Если компилятор улавливает только некоторые конфликты типов и их нет в транслируемом коде (и вы правы; я должен был быть более ясным @, чем в оригинале), интерфейсы, к сожалению, имо, [в основном?] Сахар.Вот еще один способ принудительного преобразования типов даже между несовместимыми типами и интерфейсами, на которые компилятор TS обычно жалуется:
export function forceCast<T>(input: any): T { // ... do runtime checks here // @ts-ignore <-- forces TS compiler to compile this as-is return input; }
Затем вы можете использовать его для принудительного приведения объектов к определенному типу:
import { forceCast } from './forceCast'; const randomObject: any = {}; const typedObject = forceCast<IToDoDto>(randomObject);
Обратите внимание, что я пропустил ту часть, которую вы должны выполнять перед кастингом, чтобы упростить ее. В своем проекте я компилирую все
.d.ts
файлы интерфейса в схемы JSON и используюajv
для проверки во время выполнения.источник
Если это кому-то поможет, то у меня возникла проблема, когда я хотел рассматривать объект как другой тип с аналогичным интерфейсом. Я попытался сделать следующее:
Не прошел линтинг
const x = new Obj(a as b);
Линтер жаловался на
a
отсутствие свойств, которые существовалиb
. Другими словами,a
обладает некоторыми свойствами и методамиb
, но не всеми. Чтобы обойти это, я последовал предложению VS Code:Прошла линтинг и тестирование
const x = new Obj(a as unknown as b);
Обратите внимание, что если ваш код пытается вызвать одно из свойств, существующих в типе
b
, которое не реализовано в типеa
, вы должны реализовать ошибку времени выполнения.источник
get
иset
методы.