入门

TS入门是比较简单的,后面学习曲线会越来越陡峭,无论类型推导,还是面向对象,都是需要投入非常大精力学习的。

本节主要讲解ts入门必须要掌握的4个点。

  • 转译
  • 超集
  • 类型添加擦除
  • 如何运行

转译

编译和转译是2个概念。

  • 编译(Compilation)是指将高级语言(如Java、C++等)编写的代码转换为机器语言(二进制代码)的过程。
  • 转译(Transpilation)是指将一种编程语言的代码转换为另一种编程语言的代码的过程。

Untitled

编译和转译的目的都是将源代码转换为目标代码,但它们的输出结果和使用场景有所不同。编译通常用于将高级语言转换为机器码,以便在特定的硬件平台上直接运行。转译则更多地用于将一种编程语言转换为另一种编程语言,以便在不同的环境或浏览器中运行。

js世界历来有转译的传统,被吐槽最多的就是Brendan Eich用了10天写出来的玩具语言,你可以非常轻松的在Chrome Devtools里折腾出很多神奇的bug。另外,作为脚本语言,它其实也不是很严格,各种规范也是很混乱,比如JScript,ActionScript等曾经就都是让人分不清楚的。

到现代Web开发,从coffeescript(类Ruby)到Cappuccino(类Objective-c)到rescript(类OCaml语法),到typescript都是转译的思路。最神奇的竟然是es规范发展太快,于是诞生了Babel.js这样的现代js转译。除了转译规范,移除类型,啥也不干。

Untitled

Typescript做的主要的事儿就4件

  • 增加类型,这是babel没有的。
  • babel超集,可以说babel支持的它基本都支持,包含很多es新特性。
  • 部分增强,比如interface,这是babel没有的。
  • 转译时擦除类型,比如type和interface一样,在编译时,会被抹除。

超集

JavaScript很容易学习,而TypeScript的学习曲线较陡,需要事先有脚本编程知识。 JavaScript是一种脚本语言,而TypeScript是一种面向对象的编程语言。 TypeScript支持模块,而JavaScript不支持。 TypeScript支持静态类型,可以在编译时检查正确的类型,而JavaScript不支持。 TypeScript代码必须编译,但编译JavaScript是不必要的。 TypeScript支持可选参数函数,而JavaScript不支持。 JavaScript有一个庞大的开发者社区,而TypeScript没有。

Untitled

类型添加与擦除

对于一门语言来说,肯定离不开基本数据类型和自定义类型。ts提供了一系列的关键字作为特殊类型代号,其他的都好说,唯一让我有点兴趣的是联合类型,这非常有趣的一个特性。

  • typeof 关键字用于判断是否是某种类型
  • string 表明是字符串类型,它不同于Java,首字母是小写
  • booleanBoolean类型是不同的
  • number 直接表示数字类型,没有那么多麻烦的精度问题(0b、0O、0x指明进制问题)
  • any 是万能类型,相当于Java中的Object,全部是any相当于是普通js。所以,如果你恨ts,就可以一路any到天明
  • never 表示那些永不存在的值类型
  • object 表示非原始类型,和Java中的不太一样
  • string | number 类似这样的是联合类型,这也是非常神奇的一点。这里只允许这两种类型的转换,并且能调用的方法,要取两者交集
  • `` 之间的字符串可以使用类似shell的语法,做模版 ${}
  • readonly 这竟然是个关键字,表明只读属性
  • [propName: string]: any; 这一行代码值得研究,但不推荐这么做
  • number[] 数组和Java类似,不过这是声明后置的语法,值使用[]声明,而不是{}
  • function 函数和javascript的没什么区别,有两种声明方式。lambda对js来说肯定是强项
  • =>的语法也比较恶心人,和ES6联合起来可以写一大篇文章
  • ...rest 注意这个东西!类似Java中变参的意思
  • as 是一个关键字,我们可以理解为Java的cast,但它也仅仅是语法检查而已,运行时并无法控制。(window as any)很酷,但容易出错

由于js是一门弱类型的语言,有很多的运行时转换,就不能使用类似于Java一样的强类型转换方式,所以typescript可以在编译阶段通过语言特性增强一些类型检查的功能。而在运行时,大多数根本就没有这样的判断,所以ts更像是一个过程工具。

Untitled

那么反过来想想呢?

如何运行

1、内置runtime

<script src="https://unpkg.com/typescript@4.9/lib/typescriptServices.js"></script>
<script>
const tsCode = 'let num: number = 123;';
const jsCode = window.ts.transpile(tsCode);
document.write(jsCode);
</script>

另外一个做法

有了这个环境分离基础,那么就可以实现在浏览器端运行 ts compiler!因为 ts compiler 本身编译后也是 js,只需要提供一个浏览器端的 compilerHost 就可以,typescript 官方提供了一个虚拟文件服务包@typescript/vfs 提供浏览器端兼容的 fs 服务

import ts from 'typescript'
import tsvfs from '@typescript/vfs' // 虚拟文件服务
import lzstring from 'lz-string' // 一个压缩算法

// 从cdn创建上下文,包含了ts lib的类型库,从cdn拉取
const fsMap = await tsvfs.createDefaultMapFromCDN(
  compilerOptions,
  ts.version,
  true,
  ts,
  lzstring
)
// 可以设置一个虚拟的文件,文件名index.ts,文件内容第二个参数
fsMap.set('index.ts', '/** typescript 代码 **/')

const system = tsvfs.createSystem(fsMap)
// host是ts编译器将文件操作隔离出来的部分
// 这里可以创建一个虚拟的文件服务,不依赖nodejs,在浏览器中可用
const host = tsvfs.createVirtualCompilerHost(system, compilerOptions, ts)

// 创建编译程序
const program = ts.createProgram({
  rootNames: [...fsMap.keys()],
  options: compilerOptions,
  host: host.compilerHost,
})

官方的Playground其实就是这种原理。

2、转译成js后执行

通过编译器编译

$ npm install -g typescript
$ tsc hello.ts

通过代码

const { outputText } = ts.transpileModule(your_ts_code, {
  compilerOptions: {
    strict: false,
    sourceMap: false,
    // 其他编译选项
  }
});