TypeScript快速入门 | 青训营笔记

139 阅读5分钟

这是我参与「第四届青训营」笔记创作活动的的第2天。

结合今日的青训营课程,本文旨在让读者快速入门TypeScript(简称TS),以及介绍TS细节上的坑。下一篇将着重介绍几个常见的坑。

阅读本文你将获得以下知识:

  • 什么是动/静态类型
  • 什么是强/弱类型的语言
  • 上手TypeScript
  • TypeScript新类型简介

TypeScript入门前简介

如其名所示,TypeScript加强了JavaScript中变量类型,使其成为静态类型。

注意是变量的类型。在JS中,变量的类型是动态的。这意味着JS编译前无法确定变量的类型,但需注意,即使是在JS中,字面量如'Hello World'是静态类型,是编译前可知的,为String。而let a = 'Hello World',则a在编译前类型不知,只有在执行时才可知为String。

因此TypeScript只是将动态类型转为静态类型,TypeScript本身仍并非强类型语言,而是和JavaScript一道,都是弱类型语言。如果读者先前有学习过传统的编程语言,像C/C++、Java甚至Python等都是常见的强类型语言。

强/弱类型语言的区别在于变量类型的隐式转换。弱类型如JS、TS,允许在执行过程中隐式地改变变量的类型。

比如,在js中,我们要将一个数字转成字符串,只需:

let a = 1 + "";
console.log(typeof a); // string
console.log(a); // 1

而如果是在C/C++中,则常需要借助的sprintf()函数等来将其转化为字符串。

基本语法

类型声明

number、string、数组

let a : number = 1;
let s : string = 'Hi';
let arr : any[] = [a, s];
console.log(arr); // [1, 'Hi'];
let numArrA : number[] = [1,2,3]; // 两种数组声明方式
let numArrB : Array<number> = [4,5,6];

类型别名与接口

类型别名可以方便地定义对象的必填、选填项。

type Person {
    name: string; // 必填
    age?: number; // 选填
}
let newPerson : Person = {name: 'RHXIE'}; 
// ✅age可不填,不报错

当一个对象里所包含的属性较为复杂,有必填的、选填的、只读属性、约束任意额外属性的等等,如下所示

interface Person {
    name: string; // 必填
    age?: number; // 选填
    readonly id: string; // 只读,初始化后不能更改
    [key: string]: string | number | object | undefined; 
    // 限制整个接口中可使用的所有类型
}

let me: Person = {
    // name : 'RHXIE';
    // ⛔不填name报错:此为必填项
    
    id: "114514", // ✅允许
    others: {}, // ✅允许
    
    married : false // ⛔报错,上面没允许boolean类型
};

me.id = '1919810'; //⛔ 报错,me.id只读

联合类型("|")和交叉类型("&")

很好理解,在数学里叫“并集”与“交集”,计算机里的“或”和“与”

let a : string | numbera既可以是string也可以是number,表示“或”的关系。

如果是常量,如let b : 'male' | 'female',则表示bstring类,且值只能取'male''female'

&表示“与”的关系,在类型别名和接口中会用得到:

type Book {
    author : string;
} & ( {
    tag: 'history'; // 
    range: string; // 历史书籍的往往会有一个历史范围
} | {
    tag: 'novel';
    theme: string; // 而小说往往会有一个主题
} )
}

这样一来,如果tag是history,则会有range属性;反之如果tag是novel,则会有theme属性,而不会有range属性。其中“&”可以理解为“并集”,选择&后一个集合中的所有属性,添加到Book中。

使用TypeScript你需要了解的类型

已经在JS里出现过的不再赘述,以下是几个基础的常见TypeScript类型:

  • any
  • unknown
  • void
  • never
  • tuple
  • enum

* 现阶段建议先了解,还不需要深入,

我们逐个介绍:

  1. any。当在TS中不声明变量的类型时,你常常会见到这个词出现在你眼前。意思是变量可以为任何类型,与原生JS几乎没有区别。

但初学者不建议使用any。有人笑称人写的不是TypeScript而是AnyScript,并不稀奇。如果你现在手上有几个之前写的.html,简单将里面的JS转成TS,就发现需要重新声明一大堆变量的类型。虽然大多数都是number string Array,但一旦遇到不好确定的类型(复合类型)、不好解决的TS报错,塞个any也能完事。如果是参与项目开发,这样对项目的后续维护会造成困难,许多原本可以避免的bug将层出不穷。

当然,对于写一次就扔的TS,比如学校的作业啊,简单的playground啊,也没问题,甚至直接拿JS写更快。

如果认真地解决了TypeScript提示的错误,不仅对你认识TypeScript有帮助,而且也可以回过头来让你熟悉一下之前HTML、JS没有接触到的内容。这个具体之后会以例子提到。

  1. unknown

类似any,区别是不能随便赋值,只能再赋给unknown类型的变量。比如下面就报错

let u : unknown = 'Hello';
let s : string = 'The World!';
s = u; // Type 'Unknown' is not assignable to type 'string'

相当于是一个有类型安全的any,没有与其他类型隐式转换的方式。但赋值给同为unknown的变量是可以的,

  1. void

在强类型语言里很常见。一般用于声明函数不返回除了nullundefined之外的任何值。

  1. never

简而言之用于报错的函数(如throw new Error("ERROR")),函数不返回任何值。

  1. tuple

元组,在Python里很常用,表示长度不变的向量。

  1. enum

枚举类型,用于添加一类常量,提高代码可读性,比如:

enum Job {
    FRONT_END = 0,
    BACK_END = 1,
    SDET = 2,
    QA = 3
}
PeopleArray.push({name: 'RHXIE', job: Job.BACK_END})
// 而如果只用0 1 2 3来代替Job里的各个枚举,可读性就稍差

TypeScript官方列了一个表,可以参考上面提到一些类型的赋值情况(颜色暂时无法画出来,建议查看官网,绿色的代表需要关掉strictNullChecks):链接

anyunknownobjectvoidundefinednullnever
any →
unknown →
object →
void →
undefined →
null →
never →

结语

以上,是新手入门TypeScript需要了解的东西。往后还会有高级的内容如类继承、Record等等。

从JavaScript入门过渡到TypeScript其实是非常友善的过程,因为TypeScript提供了any类型,允许在.ts中完全使用JS语法写下去,即使有相关报错也是能够编译执行的。而TypeScript提供的类型提高了代码的可读性,也提高了代码的稳定性。