typescript 是什么
一个工具,主要是能让 JS 开发者给 JS 代码加上独特的“注释”。当然这“注释”就是类型。
所以 TS 本质上就是 JS,只是多了一些类型“注释”。
题外话:说它本质是 JS 是错误的,因为编译器就是万能的,能把 TS 变成任何底层开发语言(webassembly、java 等都可以),但 大佬们开出 TS 的最终目的,是为了去服务 JS。
const a = 100;
const a: number = 100;
有什么用,明明还多写了代码?
1、给人“注释”,当然这个例子太简单了,假如我们导入一个依赖库,我们看类型就能知道它有什么方法和属性,而没必要去读源代码。
2、给编辑器“注释”,编辑器可以在接下来的代码里智能提示。
3、给编译器“注释”,限制接下来的代码对变量的使用。
在传统的强类型语言中,类型标识不仅仅只有限制作用,还决定着如何给变量分配内存,对运行时有影响。但在 TS 中的类型标识是不会对运行时有影响的。
因此在 TS 中即使编译出错了,但还是可以生成运行时的 JS 代码(在 tsconfig.json 中有一个noEmitOnError健,用来配置是否在出错时不生成代码)。
当然重要的还是这个限制作用和代码的智能提示,都能在一定程度上减少错误。
为什么要限制类型
这里说的作用仅限于 TS,传统强类型语言中的类型,作用不仅限于如此。
主要还是减少未知错误的发生,例如像下面的代码:
let a = '2132131';
// 省略一堆代码
// ...
a = 123321321;
// ....
// 省略一堆代码
// 访问a上面的属性或方法
a.length;
以上代码的错误只有在 JS 运行的时候,我们才能发现。
但假如我们用 TS 的话,这种错误,就会在我们写代码的时候,直接被编辑器识别。
强类型语言中的类型
说到底,类型到底是什么?为什么要有这个东西?
最开始的时候,编程面对的东西都是直接操作二进制数据。这个时候没有类型,有的只有 0 和 1。
后面慢慢的出现了汇编语言、高级程序语言。
语言中开始有了 一个 byte 的 Byte 类型、8 个 byte 的字节类型、2 个字节的短整型、4 个字节的整型...等等。
足以可见,不同的类型也决定着数据的存储。
在那个内存还只有 64k 的年代,一个 byte 都不浪费的严谨是有必要,且有意义的。
给数据分以不同的类型能够做到优化资源的配置。
在强类型语言中,指定类型,可以使得编译器优化数据的存储方式。
在 JS 这种动态语言中是否也有类型。
也是有的。只要存在着不同类型的数据,那就必然存在着类型。只是它没有强类型语言那么细致。
一般来说,在动态语言中,存在着两种数据的存放的方式。
一种是放在堆中,也就是所谓的引用类型,对应的特点是,数据大、数据可变、数据格式不定、动态分配内存空间,不连续存储。
一种是放在栈中,也就是所谓的值类型,对应的特点是,数据大小固定、格式固定、值固定、连续的存储空间。
在 JS 中,引用类型有,对象(包括但不限于数组[]、对象{}、类实例、函数等)。值类型有,数字、字符串、undefiend、null、Symbol、boolean、bigint
引用类型和值类型的区别在于,对变量另外赋值的时候,是重新拷贝一份值,还是说只是改变了引用地址。
TS 中的主要类型有哪些
由于 TS 是为 JS 而生的,所以它里面定义的类型都只是为了更好的服务 JS。
主要类型 number、boolean、string、null、undefined、any、enum、object、数组([]或者 Array两种表示方法)、Symbol、Bigint、Function 等
其中 undefined 和 null 是所有类型的子类型。言外之意如下:
// 都是合法的
const a: number = undefined;
const b: string = null;
其中 any 类型表示这个变量可以被赋值为任何数据类型
// 以下都是合法的
const b: number = 1;
let a: any = b;
a = 'ddd';
其中还有几种类型需要注意,便是 Object(大写)、Boolean、Number、String。他们和小些的区别在于,一般这些大写的表示是这些构造函数构造出的对象,小写的代表字面量对象。
interface和type
区别,一般 interface 用来定义全新的类型,type 用来定义组合类型(交叉类型|、联合类型&)
interface Demo {
a: number;
b?: string; // 可选类型
}
// d 既可以是字符串也可以是数字
type d = string | number;
// 类型别名
type e = Demo;
在 Typescript 中还有一个特殊的存在,便是Class,类,即是有效的值也是有效的类型。
范型,其实本质上就是定义类型的时候可以传参数。例如:
interface demoFunc<T> {
(num: T): T;
}
// 其中 T 就是一个类型参数,参数不同,它最终表示的类型也是不同的。
let demo:demoFunc<string> = (str: string) => str;
let demo1:demoFunc<number> = (str: number) => str;
// 不能将类型“(str: number) => number”分配给类型“demoFunc<string>”。
// 参数“str”和“num” 的类型不兼容。
// 不能将类型“string”分配给类型“number”。
// let demo: demoFunc<string> = (str: number) => str;
在 TS 中,类型具有可编程性,这也是
TS比较高级和复杂的部分。这样的灵活性使得TS能在很多复杂的边界情况下定义类型。
项目中的tsconfig.json文件是什么?与 jsconfig.json有什么区别?
jsconfig.js的唯一作用就是用来指导编辑器(vscode)如何更好的工作。与tsconfig.json的区别,就是jsconfig.json默认allowJs为true
tsconfig.json除了能够指导编辑器如何工作之外,还能配置typescript默认的编译器tsc命令工具,如何去编译代码和输出代码。
例如,我们可以通过设置removeComments为true来移除tsc命令生成的 js 代码中的注释。
tsconfig.json文件的存在,同时也标志着这是个项目根目录,当我们在项目目录下执行npx tsc时,tsc编译器工具会自动使用项目目录中的tsconfig.json来输出编译后的代码
webpack、babel 怎么处理 TS 文件
webpack 打包工具本身并不能直接识别.ts文件,需要通过 babel-loader 来处理它。
babel-loader 不同于 typescript 自带的tsc命令工具,babel-loader 只是单纯的把所有的类型标识删除掉。并不会在处理期间去检查 ts 中的类型错误。
由于 webpack 使用的是 babel 这个编译工具,因此 tsconfig.json 中的配置其实对于 webpack 这类工作流来说是没用的。
因此不能通过配置tsconfig.json,来配置我们项目的打包结果。
例如不能通过设置tsconfig.json中的removeComments来移除我们打包 bundle 中的注释。
TS 自带的编译器主要做什么
当我们安装好typescript时,我们同时也安装好了它提供的编译器。在项目中可以通过npx tsc命令来使用。
// index.ts
const add = (n1: number, n2: number) => n1 + n2;
var add = function (n1, n2) {
return n1 + n2;
};
当我们在终端执行npx tsc ./index.ts时,tsc编译器会给我们生成一个 js 文件。假如我们不带其他配置执行这句命令,那么tsc会自动生成 es5 的代码。
不管什么样的编译器,对于它来说,我们写的代码,都是字符串。无论它要分析或者做其他什么操作,都需要将其转换成 书写编译器本身那门语言所能理解的对象。那个对象便是抽象语法树,也就是 AST。
源代码 -> 扫描器 -> token 流 -> 解析器 -> AST -> 目标代码。
有了 AST,就可以做很多事情。
写 react 的 JSX、Vue 的模版语言、babel 做的转换、Less、Sass 等,都是先将源代码转换成 AST 后,最终处理 AST 结构,并且根据处理后的 AST 转换成目标代码。