Объект Cast для интерфейса в TypeScript

Я пытаюсь сделать бросок в своем коде из тела запроса в экспресс (используя связующее средство для тела-парсера) для интерфейса, но он не работает. Это можно сделать?

Это мой интерфейс:

export interface IToDoDto {
  description: string;
  status: boolean;
};

Это код, в котором я пытаюсь сделать актерский состав:

@Post()
addToDo(@Response() res, @Request() req) {
  const toDo: IToDoDto = <IToDoDto> req.body;
  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++;
}

Я могу передать все аргументы, и это будет работать нормально. Я прочитал, что в TypeScript не существует casting, но Type Assertion, поэтому он будет сообщать компилятору, что объект имеет тип x, поэтому... Я ошибаюсь? И как это сделать? Благодарю.

Ответ 1

В 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);

Ответ 2

Здесь еще один способ заставить тип-лить даже между несовместимыми типами и интерфейсами, где обычно компилятор 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 интерфейса .d.ts в JSON-схемах и использует ajv для проверки во время выполнения.

Ответ 3

Если это кому-нибудь поможет, у меня возникла проблема, когда я хотел рассматривать объект как другой тип с похожим интерфейсом. Я попытался сделать следующее:

Не прошло линтинга

const x = new Obj(a as b);

Линтер жаловался, что a не хватает свойств, которые существовали на b. Другими словами, a имел некоторые свойства и методы b, но не все. Чтобы обойти это, я следовал предложению VS Code:

Прошёл ворс и тестирование

const x = new Obj(a as unknown as b);

Обратите внимание, что если ваш код пытается вызвать одно из свойств, существующих в типе b, которые не реализованы в типе a, вы должны осознать ошибку времени выполнения.