TypeScript 初识
TIP
TypeScript 是 JavaScript 的超集,它可以编译成纯 JavaScript。用来限制变量、常量的类型。
注意:通常情况下 Ts 文件无法被直接运行,需要被编译成 js 文件。
安装 TypeScript
npm install -g typescript
全局安装 typeScript 之后,在全局就可以执行 tsc 命令编译 js 文件了
执行 tsc --init
生成 tsconfig.json,用来对编译的选项进行配置
编译 ts 文件
tsc demo.ts // 将 demo.ts 文件编译成 js
TypeScript 类型控制
在 ts 中,在为一个变量定义了数据类型之后,这个变量就不能再更改类型,在重新赋值的时候也必须是这个类型
类型推论
在声明一个变量时,如果没有指定这个这个变量的类型,那 ts 会隐式的推断类型
let a = 124
// 相当于
let a: number = 124
所以上面的例子中,不能再为变量赋予 number 之外的其他类型的值了,如果声明变量的时候没有为它赋值,则会隐式定义类型为 any
原始数据类型
定义常见原始数据类型:boolean、number、string
let a: boolean = true
let b: number = 123
let b2: number = NaN
let b3: number = Infinity
let c: string = "abc"
let d: string = `name is ${name}`
但是如果我们改下,改成下面的例子
let e: boolean = new Boolean(true) // 编译失败
实际上这个 new Boolean 实例是一个 object,而不是我们期望的 boolean 值,如果期望这个变量是一个布尔对象的话,需要这样写
let e: Boolean = new Boolean(true)
number、string 同理
void
当一个函数没有返回值时,可以将返回值的类型定为 void
function warnUser(): void {
console.log("This is my warning message")
}
定义为 void 没有什么意义,只能为它赋值为 null 或 undefined
null 和 undefined
用 null 或者 undefined 来定义类型的时候,也只能为它赋予 null 或者 undefined 的值
let a: null = null
let b: undefined = undefined
unknown
当变量类型允许变化时,可以定义为 unknown
let a: unknown = "124"
a = 124 // 此时a 的类型允许从字符串转变为 数字
any
如果暂时还不确定某个变量的类型,就可以定为 any,这时候这个变量的类型是可以动态变化的,在这个变量上访问任何属性和方法都是被允许的
let f: any = 4
f = "hello"
TODO: unknown 和 any 的区别
联合类型
联合类型可以表示变量为多种类型的其中一种,用 | 隔开
let a: string | number = 24
为变量声明了 string 和 number 类型之后,可以为这个变量赋予这两个之一类型的值,如上面的代码中,赋予了 number 的值之后,那么就不能再使用 number 类型没有的方法、属性了,如果想访问 a.length 那么是会报错的
当 ts 不知道类型时,只能使用共有的方法、属性
function (x: number | string){
return x.length // 会报错的。因为 x 有可能是 number 类型没有 length 属性
}
Literal Types
如果需要约束字符串、数字只能是某几个值中的某个,那么可以用 Literal Types
type gender = "male" | "female"
type age = 10 | 20
function getGender(gender: gender, age: age): string {
switch (gender) {
case "male":
return "男"
case "female":
return "女"
}
}
上面的例子中,在调用这个函数时,gender 和 age 都只能在给定的值中选中一个,输入其他值时 ts 会报错
数组
同类型数组
一个纯数字数组 number[]或者 Array<number>
来表示
let e: number[] = [1, 2, 3] // 纯数字数组
let f: Array<number> = [1, 2, 3] // 纯数字数组
一个比较常见的做法是,用 any 来允许数组内允许出现任意类型
let a: any[] = [123, "tom", { gender: "male" }]
元组
上面的数组只能用来表示数组项目为同类型的情况,如果数组内允许出现不同类型的情况,在 ts 中称为元组
let g: [string, number] = ["aaa", 21]
函数
有两种声明函数的方式,函数声明和函数表达式
函数声明
function fn(firstName: string, lastName: string): string {
return `${firstName} ${lastName}`
}
需要同时对函数的输入、输出都进行限制,不符合个数的参数是不被允许的
函数表达式
函数表达式的写法我们可以写成
let fn = function(x: number, y: number): number {
return x + y
}
这样可以通过编译,但这是 ts 通过类型推断自己推断出来的,完整的写法如下所示
let fn: (x: number, y: number) => number = function(x: number, y: number): number {
return x + y
}
接口定义函数
interface Func {
(x: number, y: number): number
}
let fn: Func = function(x: number, y: number): number {
return x + y
}
我们先给 fn 定义了 Func 的接口类型,那么在这个变量赋值的时候,就只能赋予这个变量符合的类型值
可选参数
如果需要可选参数的话,那可以用 ? 表示,可选参数必须要接在必传参数后面
function fn(firstName: string, lastName?: string): string {
return `${firstName} ${lastName}`
}
fn("124")
函数重载
TODO:
枚举:enum
枚举可以类比成一个对象,与对象不一样的地方是,对象只能通过 key 访问到 value。而枚举不仅能通过 key 访问 value,还能够通过 value 访问到 key 值
enum num {
one,
two,
three,
}
console.log(num[one])
如果没有给枚举项手动赋值,则会默认从 0 开始,第一项赋值为 0,第二项为 1,依次递增。当然也是可以手动赋值
enum Color {
red = 100,
green = 200,
blue = 300,
}
let c: number = Color[red] // c 为 100
let d: string = Color[200] // return 'green'
TODO: 常数枚举、静态枚举
- never 永不存在的值的类型
- object 对象类型,即为除基本类型之外的类型
- 断言 当你清楚的知道一个值得类型时,可以使用断言来使 ts 跳过检查
let someValue: any = "this is a string"
let strLength: number = (<string>someValue).length // <>写法
let strLength: number = (someValue as string).length // as写法
类
ts 在 es6 的类的基础上,又额外拓展了一些新的用法。
在 ts 可以使用三种访问修饰符,public、static、private、protected
- public 修饰的属性和方法是共有的,在任何地方都可以访问到,默认所有的属性和方法都是 public
- static 修饰的属性和方法都是通过类来访问,而不是通过类的实例来访问
- private 修饰的属性和方法是私有的,只能在类的内部使用,不能在类的外部访问
- protected 修饰的属性和方法也是私有的,与 private 不同的是,它在子类中是允许访问的
class Person {
public name
constructor(name: string) {
this.name = name
}
static num = 2
}
泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
function createArray<T>(length: number, value: T): Array<T> {
let result: T[] = []
for (let i = 0; i < length; i++) {
result[i] = value
}
return result
}
createArray<string>(3, "x")
上面函数声明的时候,没有先定义参数 value 的类型,而是用了 <T>
来指代任意输入的类型,在调用函数的时候,传入 <string>
来表明 T 指代的是 string 类型,也可以不显示传入 string 类型,让 ts 进行类型推论
类型断言
有时候你可能比 ts 更能确定一个变量的类型,可以使用类型断言,来欺骗 ts 解析器
值 as 类型
接口
我们使用 interface 来定义对象的类型,接口一般首字母大写
interface Person {
name: String
age: number
}
let tom:Person = {
name: 'tom'
age: 12
}
上面的例子中,我们定义了一个接口 Person,定义了一个变量 tom,并给它赋予了类型 Person,那么此时 tom 必须要拥有这两个属性,不能够再有其他属性,且这两个属性都得符合对应的类型
可选属性
可选属性的含义是这个属性可以不存在,如果存在的话,就必须符合这个类型
interface Person {
name: string
age?: number
}
任意属性
如果我们希望在规定的属性之外,可以额外拓展其他属性的话,可以用 propName
interface Person {
name: string
age?: number
[propName: string]: string | number | undefined
}
注意:任意属性的类型必须包含其他属性的类型,因为接口有个可选属性 age,所以 任意属性的类型还得包含 undefined,一个常见的使用方法是使用 any 类型
只读属性
如果我们希望某个属性不能够再更改,那么可以使用 readonly
interface Person {
readonly id: number
name: string
}
那么给变量赋予这个类型的时候,readonly 属性就不能够再更改了
let p: Person = {
id: 124,
name: "xx",
}
p.id = 1 // 这是不行的