Search code examples
objecttypescriptinterfacecasting

Cast object to interface in TypeScript


I'm trying to make a cast in my code from the body of a request in express (using body-parser middleware) to an interface, but it's not enforcing type safety.

This is my interface:

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

This is the code where I'm trying to do the cast:

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

And finally, the service method that's being called:

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

I can pass whatever arguments, even ones that don't come close to matching the interface definition, and this code will work fine. I would expect, if the cast from response body to interface is not possible, that an exception would be thrown at runtime like Java or C#.

I have read that in TypeScript casting doesn't exist, only Type Assertion, so it will only tell the compiler that an object is of type x, so... Am I wrong? What's the right way to enforce and ensure type safety?


Solution

  • There's no casting in javascript, so you cannot throw if "casting fails".
    Typescript supports casting but that's only for compilation time, and you can do it like this:

    const toDo = req.body as IToDoDto;
    // or
    const toDo = <IToDoDto> req.body; // deprecated
    

    You can check at runtime if the value is valid and if not throw an error, i.e.:

    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();
    }
    

    Edit

    As @huyz pointed out, there's no need for the type assertion because isToDoDto is a type guard, so this should be enough:

    if (!isToDoDto(req.body)) {
        throw new Error("invalid request");
    }
    
    this.toDoService.addToDo(req.body);