将对象转换为TypeScript中的接口


101

我正在尝试将代码的内容从express(使用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中不存在类型转换,只有Type Assertion,所以它只会告诉编译器对象是type x,所以...我错了吗?强制执行并确保类型安全的正确方法是什么?


1
请定义“它不起作用”。精确点。有错误吗?哪一个?在编译时?在运行时?怎么了?
JB Nizet

1
在运行时,无论我传递什么对象,代码都能正常执行。
伊莱亚斯·加西亚

不清楚您要问什么
Nitzan Tomer,2017年

我的问题是如何将传入的对象强制转换为类型化的对象。如果无法进行强制转换,则在运行时引发异常,例如Java,C#...
Elias Garcia

这回答了你的问题了吗?TypeScript或JavaScript类型转换
Michael Freidgeim

Answers:


143

javascript中没有强制转换,所以如果“ casting failed”,您将无法抛出。
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;IToDoDto
强制转换

10
对于通常在寻找类型断言的任何人,请不要使用<>。不推荐使用。使用as
Abhishek Deb,

JavaScript中没有强制转换,因此,如果“广播失败”,您将无法抛出。 ”我认为,更重要的是,TypeScript中的接口不可操作;它的作用是:实际上,它们是100%的合成糖。它们使概念上的结构维护变得更容易,但对转码后的代码没有实际影响,即imo,就像OP的问题所证明的那样,令人困惑/反模式。没有理由不匹配接口的东西就不能放入已编译的JavaScript中。这是TypeScript的有意识的选择(也是可怜的imo)。
鲁芬

@ruffin接口不是语法糖,但是确实做出了有意识的选择,使其仅在运行时保留。我认为这是一个不错的选择,这样就不会在运行时降低性能。
Nitzan Tomer

Tomayto tomahto?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用于在运行时进行验证。


2

如果对任何人有帮助,我就会遇到一个问题,我想将一个对象视为具有类似接口的另一种类型。我尝试了以下操作:

没有通过棉绒

const x = new Obj(a as b);

短绒棉正在抱怨a缺少上存在的属性b。换句话说,a具有的某些属性和方法b,但不是全部。要解决此问题,我遵循了VS Code的建议:

通过棉绒测试

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

请注意,如果您的代码尝试调用在typeb上未实现的type上存在的属性之一a,则应意识到运行时错误。


1
很高兴找到这个答案,但是请注意,如果您是通过网络或其他应用发送“ x”,则可能会泄漏个人信息(例如,“ a”是用户),因为“ x”仍然具有“ a”的所有属性,只是打字稿不可用。
佐尔坦Matók

@ZoltánMatók好点。另外,关于通过网络发送序列化的对象,还有一个关于JavaScriptgetset方法的Java样式获取器和设置器的参数
杰森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.