130 votes

Convertir un objet en interface en TypeScript

Je cherche à faire un cast dans mon code à partir du corps d'une requête dans express (en utilisant le middleware body-parser) vers une interface, mais cela n'applique pas la sécurité des types.

Voici mon interface:

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

Voici le code où j'essaie de faire le cast:

@Post()
addToDo(@Response() res, @Request() req) {
  const toDo: IToDoDto =  req.body; // <<< cast ici
  this.toDoService.addToDo(toDo);
  return res.status(HttpStatus.CREATED).end();
}

Et enfin, la méthode de service qui est appelée:

public addToDo(toDo: IToDoDto): void {
  toDo.id = this.idCounter;
  this.todos.push(toDo);
  this.idCounter++;
}

Je peux passer n'importe quels arguments, même ceux qui ne correspondent pas du tout à la définition de l'interface, et ce code fonctionnera très bien. Je m'attendrais à ce que, si le cast du corps de la réponse vers l'interface n'est pas possible, une exception soit lancée à l'exécution comme en Java ou C#.

J'ai lu que en TypeScript, le cast n'existe pas, seulement l'Assertion de Type, donc cela indiquera seulement au compilateur qu'un objet est de type x, alors... ai-je tort? Quelle est la bonne façon de garantir la sécurité des types ?

1 votes

Veuillez définir " ça ne fonctionne pas". Soyez précis. Y a-t-il une erreur? Laquelle? Au moment de la compilation? Au moment de l'exécution? Que se passe-t-il?

1 votes

Au moment de l'exécution, le code s'exécute normalement, avec n'importe quel objet que je passe.

0 votes

Il n'est pas clair ce que vous demandez

189voto

Nitzan Tomer Points 11798

Il n'y a pas de casting en javascript, donc vous ne pouvez pas déclencher d'erreur si le "casting échoue".
Typescript prend en charge le casting mais cela ne concerne que le temps de compilation, et vous pouvez le faire comme ceci :

const toDo =  req.body;
// ou
const toDo = req.body as IToDoDto;

Vous pouvez vérifier à l'exécution si la valeur est valide et si ce n'est pas le cas déclencher une erreur, par exemple :

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("requête invalide");
    }

    const toDo = req.body as IToDoDto;
    this.toDoService.addToDo(toDo);
    return res.status(HttpStatus.CREATED).end();
}

Editer

Comme l'a souligné @huyz, il n'est pas nécessaire d'effectuer une assertion de type car isToDoDto est un type de garde, donc cela devrait suffire :

if (!isToDoDto(req.body)) {
    throw new Error("requête invalide");
}

this.toDoService.addToDo(req.body);

0 votes

Je ne pense pas que vous ayez besoin de la mise en forme dans const toDo = req.body as IToDoDto; puisque le compilateur TS sait qu'il s'agit d'un IToDoDto à ce stade.

14 votes

Pour toute personne à la recherche d'une assertion de type en général, n'utilisez pas <>. c'est obsolète. Utilisez as

1 votes

"Il n'y a pas de conversion en javascript, vous ne pouvez donc pas lancer d'erreur si la "conversion échoue"." À mon avis, les interfaces en TypeScript ne sont pas exécutables; en fait, elles sont à 100% sucre syntaxique. Elles facilitent la maintenance des structures conceptuellement, mais n'ont aucun impact réel sur le code transpilé -- ce qui, à mon avis, est incroyablement déroutant/anti-pattern, comme le montre la question de l'OP. Il n'y a aucune raison pour que les éléments qui ne correspondent pas aux interfaces ne puissent pas lancer d'erreur dans le JavaScript transpilé; c'est un choix délibéré (et médiocre, à mon avis) de TypeScript.

9voto

Sepehr Points 11

Voici une autre façon de forcer une conversion de type même entre des types et des interfaces incompatibles où le compilateur TS se plaint normalement:

export function forceCast(input: any): T {

  // ... effectuer des vérifications en temps d'exécution ici

  // @ts-ignore <-- force le compilateur TS à compiler cela tel quel
  return input;
}

Ensuite, vous pouvez l'utiliser pour forcer la conversion d'objets vers un certain type:

import { forceCast } from './forceCast';

const randomObject: any = {};
const typedObject = forceCast(randomObject);

Notez que j'ai laissé de côté la partie où vous êtes censé effectuer des vérifications en temps d'exécution avant la conversion pour réduire la complexité. Ce que je fais dans mon projet, c'est compiler tous mes fichiers d'interfaces .d.ts en schémas JSON et utiliser ajv pour valider en temps d'exécution.

4voto

Jason Points 159

Si cela aide quelqu'un, j'avais un problème où je voulais traiter un objet comme un autre type avec une interface similaire. J'ai essayé ce qui suit:

N'a pas passé le linting

const x = new Obj(a as b);

Le linter se plaignait que a manquait de propriétés qui existaient sur b. En d'autres termes, a avait certaines propriétés et méthodes de b, mais pas toutes. Pour contourner cela, j'ai suivi la suggestion de VS Code:

Passé le linting et les tests

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

Notez que si votre code tente d'appeler l'une des propriétés qui existe sur le type b qui n'est pas implémenté sur le type a, vous devriez remarquer une erreur d'exécution.

1 votes

Je suis content d'avoir trouvé cette réponse, mais notez que si vous envoyez 'x' sur le réseau ou vers une autre application, vous pourriez divulguer des informations personnelles (si 'a' est un utilisateur par exemple), car 'x' conserve toutes les propriétés de 'a', elles sont simplement indisponibles pour typescript.

0 votes

@ZoltánMatók bon point. De plus, en ce qui concerne l'envoi de l'objet sérialisé sur le réseau, il y a un argument en faveur des getters et des setters de style Java par rapport aux méthodes get et set de JavaScript.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X