前言
写惯了动态语言 js 的前端开发,突然切换使用 Typescript 静态类型语言,编译时就会检查类型,这样可能会一下子不习惯。下面我将通过类比大家熟悉的语言来具体介绍 Typescript 的预备知识。接下来咱们一起来愉快地渡过预备期吧~

预备知识
1、TS与JS的关系

Typescript 是 JavaScript 的超集,它支持所有 JavaScript 的特性,并且在其上层增加了 Typescript 类型系统。
Typescript 类型系统,被设计为”可选的“,这就意味着:所有合法的 JavaScript 代码都是合法的 Typescript 代码。
2、TS的编译过程
我们可以通过对比 C++ 编译过程来理解 TS 的编译过程,如下图所示:

注意:”TS的类型检查“ 与 ”生成JS“ 是2个独立的过程。无论类型检查是否出错都会生成 JavaScript 代码!
3、类型系统
一般编译型语言都有其类型系统,可分为2类:一种是结构类型系统;一种是名义类型系统。下面我们具体来看看吧。
结构类型系统
结构类型系统(Structural Type System),是通过类型的实际结构确定两个类型是否相等或兼容。(采用该类型系统主要有:TypeScript,Haskell,OCaml,Go,…)
class Foo {
public hi(){
console.log("hi")
}
}
class Bar {
public hi(){
console.log("hello")
}
}
const a : Foo = new Foo()
const b : Bar = a
b.hi() //输出:hi
上述ts代码:创建Foo的实例并赋值给 a,a 又 赋值给类为 Bar 的 b,b可以直接执行 Foo 类的 hi 方法,正常工作不报错。可见类 Foo 和 Bar,虽然类名不一样,但结构相同,都有hi 的方法,因此属于相同的类。
名义类型系统
名义类型系统 (Nominal Type System),是通过类型的名称确定两个类型是否相等。(采用该类型系统主要有:C, C++, Java, C#, Rust, …)。
#include <iostream>
class Foo {
public:
void hi() { std :: cout << "Hi" << std :: endl;}
};
class Bar {
public:
void hi() { std :: << "Hello" << std::endl;}
}
int main (){
Foo foo;
Foo & fooRef = foo; //works!
//Error:type 'Bar' cannot
//bind to a value of unrelated type 'Foo'
Bar & bar = foo;
return 0;
}
在上述 C++ 代码:虽然 Foo 和 Bar 结构相同,但是类名不同,不能完成赋值。
4、类型注解
不同类型的语言有着不同的类型注解语法。下面我们来看看以下demo:
// C++
int printf (const char*, ...)
# Objective-c
- (id)initWinthInt:(int)value;
//Julia
commission(sale::Int , rate::Float64)::Float64
#TypeScript
function log(message: string):void
- C++ 放在参数/函数前面
- Objc 放在前面,加括号
- Julia 放在后面,加双冒号
- TS 也放在后面,加单冒号
5、类型与集合的关系
我们在上学的时候,数学有老师有讲过集合的概念。其主要分为几类:空集、单元素集合、有限集合、无限集合、全集。我们可以类比集合来学习 TS 类型。

空集
never = Ø = {}
单元素集合
Null = {null}
Undefined = {undefined}
Literal Type(字面量类型,'a',1,true)
有限集合
Boolean = {true,false}
无限集合
String = {'a','b', ... 'hello', ...}
Symbol = {Symbol(...), ...}
BigInt = {..., -1n,0n,1n,...}
Number = {-Infinity,-(2^53 - 1),...0,...+(2^53 - 1),Infinity} 和 NaN
全集
unknown = Universal set
6、类型联合与交叉
TS 联合与交叉类型也可以通过数学集合来类比学习:
| 名字 | 联合类型(Union Types) 集合并集(Union) | 交叉类型(Intersection Types)集合交集(Intersection) |
|---|---|---|
| 图示 | ![]() | ![]() |
| TS 写法 | A | B(A或B) | A & B(A与B) |
| 数学写法 | A U B(A并B) | A ∩ B(A交B) |
7、类型别名
TS 类型别名也可以通过类比的方式学习:
- JS中:我们可以使用 let, const, var 声明变量或常量。
- TS中:我们可以使用 type 为类型声明别名。
// Value Space
let ID = 9527
const PI = 3.14
var myPI = PI
// Type Space
type ID = string
type User = {
id:ID;
name:string
}
//Error: Duplicate identifier 'User'
type User = {}
//块级作用域
{
//OK
type User = {
id:number;
name:string
}
}
注意:TS 类型别名和let 变量类似,有着块级作用域。因此,同一个作用域内不能重名使用,不然会报错的。
8、类型拓宽、收窄
类型拓宽
当你用字面量赋值给let,var变量时,TS不用字面量类型作为该变量的类型,而是从字面量类型拓宽到响应的更宽泛的类型,这个过程叫做类型拓宽。
类型收窄
在某些情况下,TS可以更加确定变量的类型,此时它会将变量类型收窄。

TS试图在类型确定性与灵活性之前取得平衡
TS提供一系列方法来帮助收窄类型,以提高类型的确定性:
null check,as const,instanceof,typeof属性检查、Tagged Union 、用户类型守卫(User-defined Type Guard)、代码流分析。
9、值空间与类型空间

- 所谓的类型空间是编译期存在的各种类型,这个空间是编译器 tsc 创建的。编译成 js 后有可能被擦除。
- 值空间是 js 的 V8 引擎在运行的时候创建的,里面存在了各种类型的值
如何判断符号是在哪个空间?
- 转译后消失的符号 -> 类型空间
- 作为类型注解.别名的符号 -> 类型空间
- 类型断言后的符号 -> 类型空间(target as/is HTMLElement)
- const let var后面的符号 -> 值空间
- class enum namespace 后的符号 -> 值空间+类型空间
常用关键字、运算符在值空间和类型空间的不同含义
-
this关键字
在值空间,this指向比较复杂
在类型空间,this可以作为类方法的返回值来实现链式调用
-
& | 运算符
在值空间表示"按位与" 和 “按位或”
在类型空间表示类型的交叉和联合
-
const
在值空间用来声明常量
在类型空间与as连用 即as const 常量断言 收窄类型
-
extends
在值空间用于定义子类
在类型空间用来进行类型约束或者接口继承
-
in
在值空间用于for循环和判断属性是否存在
在类型空间用于映射类型的key的声明
总结
上述我们一起学习完 Typescript 的预备知识,这时候对 TS 有了个大概的认知,那么后续我们学习 TS 正式知识就没那么困难啦!


470

被折叠的 条评论
为什么被折叠?



