Typescript-在运行时检查类型并警告错误

时间:2018-07-31 10:18:46

标签: angular typescript

简介

我正在使用Angular 6。

Angular使用打字稿,因此您可以键入函数的参数:

public fun1(aaa: number) {
  console.log(aaa);
}

如果我尝试使用其他类型的参数调用fun1-我会收到错误消息:

public fun2() {
  this.fun1([1, 2, 3]); // TS2345: Argument of type 'number[]' is not assignable to parameter of type 'number'
}

问题

如果我可以控制文件中的参数,则类型检查效果很好。

但是也许fun1带有一些我从后端获得的参数。

Typescript在运行时无法正常运行,因此不会显示任何错误,fun1的代码只能与[1, 2, 3]一起运行。

-已编辑-

后端响应不是问题的唯一来源。有时,库可以更改您的数据类型,而您可能对此一无所知。一个示例是将Reactive Forms与应为数字的控件一起使用-编辑数字时,它将转换为字符串。

问题

在运行时是否有一些通用的类型检查方法?

我想到了类似的东西

public fun1(aaa: number) {
  if (typeof aaa !== 'number') {
    console.warn('wrong type');
  } else {
    console.log(aaa);
  }
}

,但是WebStorm然后告诉我typeof check is always false: 'aaa' always has type 'number'

同样在每个函数的顶部放置类似的内容似乎在添加很多不必要的代码。

有人对此有好的解决方案吗?

1 个答案:

答案 0 :(得分:3)

TypeScript(根据设计)不提供运行时类型信息(请参阅https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals,非目标,第5点)。

如果您向TypeScript提供一些外部数据(可能是以HTML的数据属性传递或从REST API获取),则TypeScript无法知道其类型。但是有一些解决方案。我们将看看其中的两个。

您可以采用的一种方法是使用as运算符规避类型系统。这不是一种类型安全的方法(在类型系统中引入了一个漏洞),但是(至少部分地)符合TypeScript目标(请参见上面的链接)。

我们假设fetchApiSync函数调用仅返回一个简单的javascript对象{name: "John", age: 42},我们有一个函数:

function introduceMe(person: Person) {
    return `My name is ${person.name} and I'm ${person.age} years old.`
}

以及界面

interface Person {
    name: string;
    age: number;
}

现在让我们执行以下操作:

const person: Person = fetchPersonFromApi(1);

很显然,我们希望fetchPersonFromApi返回一个与Person接口匹配的值,以便我们可以这样做:

const introduction = introduceMe(person);

要实现这一目标,我们可以执行以下操作:

function fetchPersonFromApi(id: number): Person {
   // it's synchronous and without error handling just for brevity
   const response: Person = fetchApiSync(`/persons/${id}`) as Person

   return response;
}

当我们使用as Person时从API到Person的所有内容进行显式转换时,它都会进行类型检查。这是“必需的”,因为fetchApiSync无法知道从外部API获取的值的类型。当然,当我们遇到其他类型的值而不是Person类型的值时,就会遇到问题,但是我们无能为力……还是可以?

此选项的另一个变体是使用any(或TS 3中的unknown)类型。这两个允许您跳过类型检查-类型any的值可以分配给“任何”类型,包括Person。但是,由于类型系统内部具有未经检查的值,因此很难跟踪错误。明智地使用它!

您可以采用的第二种方法是使用io-ts库(https://github.com/gcanti/io-ts)。它允许您指定runtime types并在运行时执行类型检查(因此,它引入了验证值是否与类型匹配的开销)。此外,您需要以略有不同的方式指定这些类型(请参阅文档),以便它们可以在运行时存在。值得注意的是,io-ts允许您从io-ts类型定义派生纯TypeScript类型。使用io-ts可使您保持更多类型安全,并保护代码库免于讨厌的转换或使用any类型(或TypeScript 3中的unknown类型)。这样做需要付出一定的代价-其中一部分是不可忽略的复杂性。