Передати об’єкт на інтерфейс у TypeScript


95

Я намагаюся зробити привід у своєму коді з тіла запиту в експрес-форматі (за допомогою проміжного програмного забезпечення body-parser) до інтерфейсу, але це не забезпечує безпеки типу.

Це мій інтерфейс:

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 кастинг не існує, існує лише твердження типу, тому він лише повідомляє компілятору, що об’єкт має тип x, тож ... Я помиляюся? Який правильний спосіб забезпечити та забезпечити безпеку типу?


1
Будь ласка, визначте "це не працює". Будьте точні. Чи є помилка? Який? Під час компіляції? Під час виконання? Що сталося?
JB Nizet

1
Під час виконання код виконується нормально, незалежно від того, який об'єкт я передаю.
Еліас Гарсія,

Незрозуміло, про що ви питаєте
Nitzan Tomer

Моє питання полягає в тому, як передати вхідний об'єкт набраному об'єкту. Якщо акторський склад неможливий, викиньте виняток під час виконання, наприклад, Java, C # ...
Еліас Гарсія,

Це відповідає на ваше запитання? Кастинг TypeScript або JavaScript
Michael Freidgeim

Відповіді:


136

У 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на даний момент
huyz

9
для тих, хто взагалі шукає твердження про тип, не використовуйте <>. це застаріло. використанняas
Абхішек Деб

" Немає кастингу в javascript, тому ви не можете кидати, якщо" кастинг не вдається ". " Я думаю, що більш суттєво, інтерфейси в TypeScript не можуть діяти; насправді вони 100% синтатичний цукор . Вони полегшують концептуальне підтримання структур , але фактично не впливають на перекладений код - що, безумовно, шалено заплутує / протидіє, як свідчить питання OP. Немає жодної причини, через яку речі, які не відповідають інтерфейсам, не могли перекинути перекладений JavaScript; це свідомий (і поганий, imo) вибір TypeScript.
jorffin

Інтерфейси @ruffin не є синтаксичним цукром, але вони зробили свідомий вибір, щоб тримати його лише під час виконання. я думаю, що це чудовий вибір, таким чином, за час роботи не передбачено штрафу за продуктивність.
Ніцан Томер

Томайто томахто ? Безпека типу з інтерфейсів у TypeScript не поширюється на ваш перекладений код, і навіть до виконання програми безпека типу сильно обмежена - як ми бачимо у випуску OP, де взагалі немає ніякої безпеки типу . TS міг сказати: "Гей, почекай, anyще не гарантовано IToDoDto!", Але TS вирішив не робити. Якщо компілятор виявляє лише деякі конфлікти типів, а жоден у перекладеному коді (і ви маєте рацію; я мав би бути більш чітким @ що в оригіналі), інтерфейси, на жаль, imo, [в основному?] Цукор.
рюш

7

Ось ще один спосіб примусити типову передачу навіть між несумісними типами та інтерфейсами, на які зазвичай скаржиться компілятор 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для перевірки під час виконання.


1

Якщо це комусь допомагає, у мене виникла проблема, коли я хотів розглядати об’єкт як інший тип із подібним інтерфейсом. Я спробував наступне:

Не пройшов футеровку

const x = new Obj(a as b);

Лінтер скаржився на aвідсутність властивостей, які існували на b. Іншими словами, aмав деякі властивості та методи b, але не всі. Щоб обійти це, я слідував пропозиції VS Code:

Пройшов лінійку та випробування

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

Зверніть увагу, що якщо ваш код намагається викликати одне з властивостей, яке існує для типу, bщо не реалізовано для типу a, ви повинні усвідомити помилку виконання.


1
Я радий, що знайшов цю відповідь, але зауважте, що якщо ви надсилаєте 'x' через мережу або інший додаток, ви можете просочувати особисту інформацію (якщо 'a' є користувачем, наприклад), тому що 'x' все ще має всі властивості 'a', вони просто недоступні для машинопису.
Золтан Маток

@ ZoltánMatók хороший момент. Крім того, щодо надсилання серіалізованого об'єкта через мережу існує аргумент для методів отримання і встановлення стилю Java над JavaScript getта setметодами.
Джейсон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.