为什么强制类型转换比显式类型变量声明的类型安全性小?

时间:2019-02-22 21:24:33

标签: typescript

我喜欢打字安全,这就是为什么我喜欢打字稿。

但是,我发现最终不得不明确地 声明额外的变量以获得我需要的类型安全。


更新示例:

假设我想在一个字符串数组上调用“ .join”,并且我真的想确保它是一个字符串数组(而不是Objects,最终将其输出为{{1} }):

如果我只是在这里做这段代码,它不会阻止我意外地为db.getCollection('interviews').updateMany({ comments: null }, { $set: { comments: [] }}) 传递对象:

如果我为[object Object]创建一个数组并将其键入为somethingElse,则可以避免该问题:

urlParts

但是,我似乎没有一种方法可以创建一个单独的string[]变量,而我一直希望避免这样做。例如,通过以下方式之一:

const baseUrl = "https://stackoverflow.com/"
const somethingElse = { not: "a string" };
const urlParts: string[] = [baseUrl, somethingElse];
window.location.replace(urlParts.join("\n"))

我发现避免创建变量的唯一方法是将结果内联为 一个自执行的匿名强类型函数-但这看起来就像 一个荒谬的解决方法:

urlParts


Experimentation in TS Playground


这是TypeScript错误/漏洞吗?还是我错过了什么?

我请几个同事看看他们有什么想法, 但是我们无法弄清楚如何获得更严格的类型。

谢谢!

1 个答案:

答案 0 :(得分:7)

Type assertions的安全性不如type annotations,因为虽然注释和断言都允许您扩大表达式的类型(可以安全读取),但只有类型断言才允许您缩小表达式的类型,这对于阅读是不安全的。编译器可以执行此操作,因为您断言这是真的。

作为一个示例...假设您获得了类型为x的变量string。如果您只是在读取该变量的值,可以将变量视为更广泛的类型,例如string | number。这是因为string类型的所有值也是string | number类型的也是值。因此let y: string | number = xx as string | number都是有效的,因为编译器可以验证这些表达式的有效性。

另一方面,如果为您提供类型为y的变量string | number,则将该变量视为更窄不安全 >像string这样输入。这是因为存在string | number类型的值,而不是类型string的值。因此let x: string = y将给出编译器错误;编译器知道将string | number视为string是不安全的。但是允许y as string,因为您告诉编译器您知道的不止于此。有时,编译器会推断出比您更广的类型,例如let y = true ? "a" : 1;。 “显然” y将是string,但是编译器没有注意到(从TS3.3起)仅对三元运算符的真实分支进行求值,因此它推断出{{1} } string | number。因此,您可以断言y是安全的,编译器将相信您。

此外,如注释中所述,术语“类型断言”比“类型强制转换”更可取,通常是因为“类型强制转换”有时暗示某些运行时效果。 TypeScript的类型系统在运行时完全是erased,因此只有在设计时,类型系统才能执行任何操作。断言是指告诉编译器如果无法验证特定表达式是否具有特定类型,则不必担心。如果您的主张正确,那么一切都很好。如果您的断言不正确,则在设计时所有似乎都可以正常运行,然后您将收获任何运行时后果。

类型断言并非完全 不安全;如果为y as string推断的类型既不比类型x as T窄也不宽,则编译器仍然可能不主张断言x。例如,如果您拥有类型T的变量z并编写了string,则编译器仍会警告您。在这种情况下,您仍然可以通过指出在继续操作之前始终可以先将其扩展为公共的超类型或缩小为公共的子类型来迫使编译器提交。类型unknown是所有类型的通用超类型,因此您始终可以编写z as number并忽略编译器的任何反对意见。

还要注意,类型断言的z as unknown as number语法与x as T语法完全相同。有两种写方法的唯一原因是angle brackets don't play nicely with JSX

好的,希望能有所帮助;祝你好运!


更新:我认为以内联方式获得类型注释的安全性的最乏味的方法是使用辅助函数。这并不像您正在使用的IIFE那样疯狂,但是它是相似的。这是我的处理方式:

<T> x

函数const ann = <T>(x: T) => x; (“注释”的缩写)使您可以显式注释传入参数的类型。让我们看看:

ann

这对您来说足够好吗?