【Typescript】- Typescript预备知识

「这是我参与11月更文挑战的第3天,活动详情查看:2021最后一次更文挑战

前言

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

48e589e19ebb44d382455ac1e0344cf5_tplv-k3u1fbpfcp-watermark.gif

预备知识

1、TS与JS的关系

image.png

TypescriptJavaScript 的超集,它提供了所有 JavaScript 的特性,并且在其上层增加了 Typescript 类型系统。

Typescript 类型系统,被设计为”可选的“,这就意味着:所有合法的 JavaScript 代码都是合法的 Typescript 代码。

2、TS的编译过程

我们可以通过对比 C++ 编译过程来理解 TS 的编译过程,如下图所示: image.png

注意:”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的实例并赋值给 aa 又 赋值给类为 Barbb可以直接执行 Foo 类的 hi 方法,正常工作不报错。可见类 FooBar,虽然类名不一样,但结构相同,都有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++ 代码:虽然 FooBar 结构相同,但是类名不同,不能完成赋值。

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 类型。

image.png

空集

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)
图示 image.png image.png
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可以更加确定变量的类型,此时它会将变量类型收窄image.png

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

9、值空间与类型空间

image.png

  • 所谓的类型空间是编译期存在的各种类型,这个空间是编译器tsc创建的。编译成js后有可能被擦除。
  • 值空间是js的V8引擎在运行的时候创建的,里面存在了各种类型的值

如何判断符号是在哪个空间?

  1. 转译后消失的符号 -> 类型空间
  2. 作为类型注解.别名的符号 -> 类型空间
  3. 类型断言后的符号 -> 类型空间(target as/is HTMLElement)
  4. const let var后面的符号 -> 值空间
  5. class enum namespace 后的符号 -> 值空间+类型空间

常用关键字、运算符在值空间和类型空间的不同含义

  • this关键字

    在值空间,this指向比较复杂

    在类型空间,this可以作为类方法的返回值来实现链式调用

  • & | 运算符

    在值空间表示"按位与" 和 "按位或"

    在类型空间表示类型的交叉和联合

  • const

    在值空间用来声明常量

    在类型空间与as连用 即as const 常量断言 收窄类型

  • extends

    在值空间用于定义子类

    在类型空间用来进行类型约束或者接口继承

  • in

    在值空间用于for循环和判断属性是否存在

    在类型空间用于映射类型的key的声明

总结

上述我们一起学习完 Typescript 的预备知识,这时候对 TS 有了个大概的认知,那么后续我们继续学习 TS 正式知识就没那么困难啦!

猜你喜欢

转载自juejin.im/post/7035206951084490789