从 JavaScript 到 TypeScript 2 - 基础特性和类型推导

随着应用的庞大,项目中 JavaScript 的代码也会越来越臃肿,这时候许多 JavaScript 的语言弊端就会愈发明显,而 TypeScript 的出现,就是着力于解决 JavaScript 语言天生的弱势:静态类型。

前端开发 QQ 群:377786580

这篇文章首发于我的个人博客 《听说》,系列目录:

在上一篇文章 《从 JavaScript 到 TypeScript 1 - 什么是 TypeScript》 中我们讨论了什么是 TypeScript。这一篇文章我们来介绍 TypeScript 一些基础类型约束。

基础类型

我们先简单的声明一些变量:

1
2
3
4
5
let a: number
let b = true // 有默认值的情况,甚至不需要声明类型,ts 会自动推导
let c: [string, number] // 元组
enum Color {Red, Green, Blue} // 枚举
let d: { name: string } = { name: 'linkFly' }

当我们给这些变量赋错误的类型值的时候,会抛出类型错误异常。

错误提示

是不是很简单,TypeScript 优秀的设计使得即使你没有接触过它,但是仍然能够读懂它。

复杂类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// array
let list_a: number[] = [1, 2, 3]
let list_b: Array<number> = [1, 2, 3] // number 类型的数组
let list_c: [string, number] = ['linkFly', 0]

// any
let notSure: any = 4
notSure = true // any 类型可以自由赋值

// 函数类型
let fn: (id: string) => number = (id) => 1
// 这里使用了 ECMAScript 6 的箭头函数,和下面的代码等价
let fn: (id: string) => number = function (id) {
return 1
}

类型推导

高级类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 联合类型, foo 是 string 或 number
let foo: string | number = 1

// 类型断言,强制使用兼容类型中的某一类型
(foo as string)

// 类型保护(判断)
if (typeof foo === 'string') {
// dosomething
}
// 类型保护(判断)
if (foo instanceof String) {
// dosomething
}

类型保护

Model

从前几年热门的 MVC 一直到现在热门的 MVVM,我们发现无论是 MVC(Model-View-Controller) 还是 MVVM(Model-View-ViewModel),我们始终抛不开一个关键的地方 —— 数据层:Model

因为本质上整个页面的操作都是在进行数据流动,页面展现本质上都是数据,而我们通过 Model 来描述数据。

这是一个简单的 Model 演示:

1
let user : { id: number, name: string } = { id: 1, name: 'linkFly' }

在 TypeScript (或者是所有强 OO 语言)中,推荐以 Model 来描述数据的方式也就是 Class

这一小节只简单介绍 Class 和 泛型,实际项目中可能还会牵扯更多更强大的 OO 概念:接口、抽象类、继承类、继承属性。

这些知识不是一蹴而就的,而是需要在项目中不断探索不断组合的。

Class 类

所有类型的根本都是类,TS 中声明一个类的语法非常简单,可读性很高。

注意,TS 中类型是核心,当你想把一个项目从 JavaScript 迁移到 TypeScript 的时候,需要为项目中补充大量的类型,而这些类型大部分都是基于 Class 构建的。

这是一个简单的类:

1
2
3
4
5
6
class User {
id: number
name: string
}

let user: User = { id: 1, name: 'linkFly' }

当然随着需求的不同,也可以补充很多细节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class User {
// 只读属性
readonly id: number

// 存取器, get/set
private _name: string
get name(): string {
// dosomething
return this._name
}
set name (name: string) {
console.log('this is set method')
// dosomething
this._name = name
}

// 构造函数
constructor (id: number, theName: string) {
// 只读属性只能在构造函数里初始化
this.id = id
this._name = theName
}

// 实例方法
say () {
console.log(`name: ${this.name}`)
}

// 静态方法(类方法)
static print () {
console.log('static method')
}
}

let user = new User(1, 'linkFly')
user.name = 'tasaid' // 会输出 'this is set method'
user.say() // 实例方法
User.print() // 静态方法

Model_User

泛型

泛型是用来解决类型重用的问题。

例如下面一个函数,只能传递 number 的参数并返回:

1
2
3
4
function identity(arg: number): number {
// dosomething
return arg
}

现在想传递一个 string 类型的参数,然后也返回它,这个时候就可以使用泛型,使用泛型可以接收任意类型并返回:

1
2
3
4
5
6
7
8
9
10
// 这个 T 就是泛型,也可以叫其他名字
function identity<T>(arg: T): T {
// dosomething
return arg
}

identity<string>('linkfly')
identity('linkfly') // 自动推导
identity(0)
identity(true)

我们可以轻松的使用泛型来实现数据包装:

1
2
3
4
5
6
7
8
9
10
11
12
13
function fetch<T>(url: string): Promise<T> {
// 远程请求数据并返回结果
return http(url).then(data => {
return data as T
})
}

class User {
name: string
}

// 泛型使用
let user = fetch<User>('https://tasaid.com/user')

小 tips

  • TypeScript 中文网 可以看到完整 TS 类型。
  • 在项目初期使用 TS 中,会需要很大的时间和精力,去编写和架构基本业务类型(Models),在此之后会越来越方便快捷。
  • 一些没有行为只需要做类型检查的类型(没有方法的 Models),可以使用 TypeScript 声明文件 (*.d.ts),例如:
1
2
3
4
5
6
declare namespace Models {
interface GPS {
lat: number
lng: number
}
}

这个系列的文章不会讲解 TypeScript 的声明文件,但是它是 TypeScript 中不可缺少的一部分。

  • 尽量减少使用 any 类型,它意味着类型不可控
  • 某些变量或者第三方库中属性无法感知,使用 as 强制进行类型推导即可。
1
2
3
4
5
window.tempName = 'linkFly' 
// Errors: [ts] Property 'tempName' does not exist on type 'Window'.

// 强制推导
(window as any).tempName = 'linkFly'

至此,我们对 TypeScript 类型、Model 已经有了一定的了解,在下一篇,我们将了解如何引入和编译 TypeScript:《从 JavaScript 到 TypeScript 3 - 引入和编译

 

TypeScript 中文网:https://tslang.cn/

TypeScript 视频教程:《TypeScript 精通指南


从 JavaScript 到 TypeScript 2 - 基础特性和类型推导
https://tasaid.com/posts/29102f33/
作者
linkfly
发布于
2017年10月11日
更新于
2019年4月25日
许可协议