将对象转换为 TypeScript 中的接口

Posted

技术标签:

【中文标题】将对象转换为 TypeScript 中的接口【英文标题】:Cast object to interface in TypeScript 【发布时间】:2017-10-09 05:24:49 【问题描述】:

我正在尝试将我的代码从 express 中的请求主体(使用主体解析器中间件)转换为接口,但它没有强制执行类型安全。

这是我的界面:

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,所以......我错了吗?执行和确保类型安全的正确方法是什么?

【问题讨论】:

请定义“它不工作”。要精确。有错误吗?哪一个?在编译时?在运行时?会发生什么? 在运行时,无论我传递什么对象,代码都会正常执行。 不清楚你在问什么 我的问题是如何将传入的对象转换为类型化的对象。如果无法进行强制转换,请在运行时抛出异常,如 Java、C#... 这能回答你的问题吗? TypeScript or javascript type casting 【参考方案1】:

javascript 中没有强制转换,因此如果“强制转换失败”,您将无法抛出。 Typescript supports casting 但这只是为了编译时间,你可以这样做:

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 对于寻找类型断言的人来说,不要使用 。这已被弃用。使用as "javascript 中没有强制转换,所以如果“强制转换失败”,你不能抛出。" 我认为,更重要的是,TypeScript 中的接口是不可操作的;事实上,他们是 100% syntatic sugar。它们使概念上的结构更容易维护,但对转译的代码没有实际影响——正如 OP 的问题所证明的那样,imo 非常混乱/反模式.没有理由不匹配接口的东西不能在转译的 JavaScript 中抛出;这是 TypeScript 的一个有意识的(和可怜的,imo)选择。 @ruffin 接口不是语法糖,但他们确实做出了有意识的选择,只将其保留在运行时。我认为这是一个不错的选择,这样在运行时不会降低性能。 Tomayto tomahto? TypeScript 中接口的类型安全不会扩展到您的转译代码,甚至在运行前,类型安全也受到严重 限制——正如我们在 OP 的问题中看到的那样,没有类型安全。 TS 可以说,“嘿,等等,你的 any 还不能保证是 IToDoDto!”,但 TS 选择不这样做。如果编译器只捕获 some 类型冲突,在转译的代码中没有(你是对的;我应该在原版中更清楚@那个),不幸的是,imo,[主要是?] 糖。【参考方案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 接口文件编译成 JSON 模式,并使用 ajv 在运行时进行验证。

【讨论】:

【参考方案3】:

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

没有通过 linting

const x = new Obj(a as b);

linter 抱怨 a 缺少 b 上存在的属性。换句话说,ab 的一些属性和方法,但不是全部。为了解决这个问题,我遵循了 VS Code 的建议:

通过 linting 和测试

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

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

【讨论】:

我很高兴找到了这个答案,但请注意,如果您通过网络或其他应用程序发送“x”,您可能会泄露个人信息(如果“a”是例如),因为'x'仍然具有'a'的所有属性,它们只是不能用于打字稿。 @ZoltánMatók 好点。此外,关于通过网络发送序列化对象,an argument 用于 Java 风格的 getter 和 setter,而不是 JavaScript getset 方法。

以上是关于将对象转换为 TypeScript 中的接口的主要内容,如果未能解决你的问题,请参考以下文章

打字稿:将 JSON 对象转换为类/接口对象

Typescript将string []转换为字符串

如何将字符串类型转换为 Typescript 中的一种对象类型?

typescript 将Typescript对象转换为普通对象。

映射具有未知深度的通用 TypeScript 接口

将 TypeScript 对象转换为 JSON