打字稿中的运行时类型安全

时间:2019-12-02 09:15:53

标签: angular typescript

我试图更好地理解Typescript中的类型安全性,并遇到了具有此功能的这种情况:

function test(x: number){
    console.log(typeof x);
}

如果我以这种方式调用此方法-test('1')会引发编译时错误,但如果将其更改为此,它将很好地工作:

let y: any = '1';
test(y);
//works fine
//Outputs "string"

据我了解,将“ x”声明为数字仅在编译时有效,而Typescript仅在编译时强制执行类型安全而不是运行时。因此,我想知道我是否正确理解了这一点或缺少任何内容,还有什么是确保运行时类型安全的所有不同方法?

5 个答案:

答案 0 :(得分:2)

TypeScript保证仅在编译时进行类型检查,这是真的。

JavaScript具有动态类型,因此您可以使用typeofif自己进行检查,但是没有人使用打字稿来进行检查。

您还可以在strict中启用所有tsconfig.json选项,并且ts将加强验证规则。

UPD

您还可以使用tslinttslint-eslint-rules软件包进行代码验证,包括no-any之类的类型规则

答案 1 :(得分:2)

TypeScript是两种合并的语言:

  1. 类型级别语言
  2. 价值水平语言

第一个在所有类型注释中都可见,这是纯JavaScript中不存在的语法。每个类型注释和诸如type, interface, enum, as, in之类的保留字都是类型级别语言的一部分。 TS在编译过程中首先要检查语法和类型级别语言的语法以及值级别语言上注释的正确性。

第二种是值级语言,它是完全正确的JS语法。它还具有ECMAScript建议书第3阶段的大多数功能。

第一部分已完全删除(例外是Enum,它在运行时中具有表示形式),第二部分保留在运行时中。

回到安全问题,是的,TypeScript在编写代码时确保安全。您定义合同,编写合同的转换,并且TS正在检查与合同注释有关的代码的正确性。它消除了诸如拼写错误或使用空/未定义对象的方法和属性之类的一堆错误,并且还在程序流程中提供了可见的数据定义。

但是,它不能确保运行时的安全。所有类型注释都只是假设,如果我们将API定义为具有此类响应的端点响应,则TS将保证代码遵循该结构,但是如果在运行时结构会有所不同,则程序自然会失败,因为合同不等于数据。

回到您的示例

function test(x: number){
    console.log(typeof x);
}

将函数test定义为以number作为参数的函数,您说的没什么不同,然后number将被传递给该函数。因此,上面的实现实际上是一个常量,因为typeof x将始终返回number,因为这正是注解所说的。

// below is equal to your implementation, as number has one specific type
// caution is equal for TS, not for the runtime!
function test() {
  return console.log('number')
}

如果函数在输入方面是多态的,则应按此注释输入。您可能不知道可以得到什么输入,然后可以实施结构检查。正确的名称是-type guard。考虑下面的例子

function test(x: unknown) {
  if (typeof x === 'number') {
    return x * 2; // return number
  }
  if (typeof x === 'string') {
    return x + '- I am string'; // return number
  }

  if (typeof x === 'object') {
    return x; // return object;
  }

  return null; // for others return null
}

现在函数test推断出的输出为并集string | number | object | null。通过使用控制流和条件,TS可以了解函数返回的内容。

每次您的代码处理某种多态类型时,您都可以使用类型保护来准确指定要使用的类型。该检查由结构完成(因为在运行时中仅存在结构,因此在编写代码时仅对结构进行注释),因此可以检查typeofinstanceof或对象是否具有特定的键或值。

非常重要的事情要记住-类型是一些现有运行时结构的标签。标签/类型在运行时不存在,但是结构确实存在。这就是TS之所以能够理解类型保护的原因,因为每种类型都涉及某种结构。

答案 2 :(得分:1)

当您说let y: any = '1'时,基本上关闭了检查变量y的类型-因此您应该将其传递给需要数字的方法。如果不添加any类型,则会在编译时引发错误。 基本上您是对的,:type批注非常适合编译时类型检查。 check the typescript playground可能是TSC如何编译您的代码的有趣之处。

答案 3 :(得分:1)

运行时类型安全与TypeScript无关。 TypeScript团队非常了解该语言的目标,您可以在“ 非目标section of the wiki中阅读以下行:

  

提供其他运行时功能或库。

如果要查找运行时类型安全,则必须在其他地方查找。

最重要的是,您正在通过声明变量的类型为any来禁用想要的类型检查。您可以read about that behavior on the handbook

TS中的运行时类型检查策略与JS中的相同,因为在运行时,不再有任何TS,而全部是JavaScript。

答案 4 :(得分:1)

JavaScript具有断言功能console.assert(assertion, message?)。我通常在函数中使用它:

/**
 * Checks if the value is in range
 *
 * @param value
 * @param fromInclude lower inclusive limit
 * @param toExclude upper exclusive limit
 */
export function inRange(value: number, fromInclude: number, toExclude: number): boolean {
    console.assert(!isNaN(value));
    console.assert(!isNaN(fromInclude));
    console.assert(!isNaN(toExclude));
    console.assert(fromInclude < toExclude);

    return value >= fromInclude && value < toExclude;
}

这非常有用,可以让开发人员及早发现问题。而且,您可以配置构建管道以删除生产构建中的所有console.assert调用,这样它就更轻巧并且不会在控制台中向产品用户显示警告。您可以执行断言中的所有类型检查,因为许多类型类型转换不会导致错误,但是很高兴知道您在某个地方获取了字符串'237'而不是数字237并进行了修复。