我在日常工作中使用相当大的 Typescript 代码库。我看到人们在学习它时最挣扎的一件事是类型系统。
Typescript 类型相当特殊,因为它们存在于编译时而不是运行时。我相信将它们视为类型注释比像 Go 或 Java 这样的语言中获得的正确类型更好的思维模型。
让我举一个 Go 中的例子:
type MyType struct { A int B int }
func myFunction ( input MyType ) { bytes , _ := json . Marshal ( str ) fmt . Println (string( bytes )) }
在上面的示例中,Go 类型系统保证传入函数的任何内容都将具有A
和B
字段,它们都是整数,并且不会有其他字段。其他任何东西都不会编译。
为了比较,看看这个类似的 Typescript 代码:
type MyType = { a : number ; b : number ; }
function myFunction ( input : MyType ){ console . log ( JSON . stringify ( input )) }
如果您像我一样从强类型语言背景进入 Typescript,您可能会认为这两段代码会为myFunction
函数提供类似的保证。在 Typescript 中并非总是如此。让我们看看 Typescript 允许你用这个函数做的一些有趣的事情:
myFunction ({ a : 1 , b : 2 }); // works fine as expected, outputs {"a":1,"b":2} myFunction ({ a : 1 , b : 2 , c : 3 }); // Won't compile myFunction ({ a : 1 , b : 2 , c : 3 } as MyType ); // works fine, outputs {"a":1,"b":2,"c":3} myFunction ( 'im just a string!' as MyType ); // Won't compile, thankfully myFunction (( 'im just a string!' as unknown ) as MyType ); // Works fine, outputs "im just a string!", but at this point it is quite obvious we're doing something wrong
它实际上归结as
关键字的行为 – as
不是强制转换。你不能在 Typescript 中转换任何东西,因为运行时没有 Typescript 类型!所做as
只是告诉 Typescript 编译器“放松,编译器,我知道我在做什么”。 .
以下是上述函数的编译后的 Javascript 代码:
myFunction ({ a : 1 , b : 2 }); // works fine as expected, outputs {"a":1,"b":2} myFunction ({ a : 1 , b : 2 , c : 3 }); // Won't compile myFunction ({ a : 1 , b : 2 , c : 3 }); // works fine, outputs {"a":1,"b":2,"c":3} myFunction ( 'im just a string!' ); // Won't compile, thankfully myFunction ( 'im just a string!' ); // Works fine, outputs "im just a string!", but at this point it is quite obvious you're doing something wrong
所有类型注释都在运行时简单地删除。打字稿只有在你的程序被编译之前才存在。编译后,它又是普通的旧 Javascript。
正因为如此,在 Typescript 中,你的类型检查和你做的一样好。它们不是编译器强加的硬限制,就像在 Go 中一样。 Typescript 类型更像是对其余代码的提示。在任何时候,您都可以覆盖它们或简单地使用any
.它们可以帮助您编写有效的代码,但它们不会更改将实际执行的程序。
这是我花了一些时间来思考的事情——将 Typescript 视为具有类型语法的 Javascript 的非常高级的 linter,而不是将其视为编译成 Javascript 的适当类型语言,这是一个更好的思维模型——您编写的大多数 Typescript 输入代码不会被编译成 Javascript,它只是用于编译检查,然后被删除。如果你想在运行时使用 Typescript 进行真正的类型检查,你必须使用类型保护