TypeScript 是 JavaScript 的一个超集( Superset ),它兼容 JavaScript 的所有语法,同时在此基础上增加了静态类型系统和其他高级特性。它由 Microsoft 公司开发,为开发大型应用程序和改善代码质量而设计

TypeScript 只负责解决开发阶段所遇到的问题,它可以在开发阶段就发现程序中的错误,而不是运行阶段

TypeScript 最终会被编译成 JavaScript,以便在浏览器或其它 JavaScript 环境中运行

特点:

  • 静态类型检查( 编译期间检查数据类型 ):
    • 使用 TS 提供的静态类型系统(如 string, number, boolean, any, 自定义类型等)可以在编译时发现潜在错误
    • TS 提供类型推断功能,即使你不显式定义类型,TypeScript 也会尽量推断出变量类型
  • 现代特性支持:
    • 支持 ECMAScript 的最新特性,如 装饰器
    • 在旧版浏览器运行时可通过 TypeScript 转译为兼容的 JavaScript
  • 增强开发体验:
    • TS 支持更多高级的 IDE( 如 VSCode、WebStorm ) 功能( 如类型提示、错误检测和自动补全等功能 )
  • 完整的面向对象支持:
    • 支持类、接口、枚举、命名空间、模块和继承等高级功能

使用(Usage

  1. 初始化项目

    1
    npm init -y
  2. 安装 TypeScript 到项目中

    1
    2
    3
    4
    5
    npm i typescript -D
    # typescript 用来编译 TypeScript 代码到标准的 JavaScript 代码

    # 检查 TypeScript 是否安装完成
    npx tsc --version
  3. 初始化 TypeScript 项目

    1
    2
    npx tsc --init
    # 这条命令会在当前目录下生成一个 tsconfig.json 文件, 它是 TypeScript 的配置文件
  4. 编译 ts 文件( ts -> js )

    1
    2
    npx tsc
    # tsc 是 TypeScript 的编译器, 用来编译生成 JavaScript 代码文件, 在默认配置下, TypeScript 会将项目(包含 tsconfig.json 的目录)中所有 ts 文件(node_modules, bower_components, jspm_packages 这些目录除外)编译为 js 文件

JS 引擎与 TS 编译器的区别和对比:

  • JavaScript 引擎用于编译并执行 JavaScript 代码
  • TypeScript 编译器用于编译 TypeScript 代码并生成 JavaScript 代码

TS编译器与JS引擎的对比

类型声明(Type Declaration

类型声明是通过显式地定义变量、函数、对象或类的类型来约束它们的值和行为。指定类型后,当为变量赋值时,TS 编译器会自动检查值是否符合类型声明,符合则赋值,不符合则报错。这种静态类型的特性有助于捕获潜在错误并提高代码的可读性和可维护性

语法:

1
2
3
4
5
let 变量名: 类型
let 变量名: 类型 = 值
function fn(参数名: 类型, 参数名: 类型): 返回值类型 {
...
}
类型例子意义
number1, -20, 3.14数字类型
string"Hello", 'Wecome'字符串类型
booleantrue, false布尔类型
undefinedundefined未定义
nullnull
object{name: 'value'}对象类型
type[][1, 2, 3, 4]数组元素的类型
any*任意类型
unknown*未知类型( 类型安全的any )
voidundefined没有类型,函数没有返回值
never不能有值不能是任何值,函数不能有返回值
tuple[5, 6, 7, 8]元组类型
enumenum{A, B}枚举类型

基本使用:

1
2
3
4
5
6
7
8
9
let num: number = 25 // 数字
let isDone: boolean = true // 布尔值
let name: string = "TypeScript" // 字符串
let u: undefined = undefined // 未定义
let n: null = null // 空
let o: object = { name: 'Timeic' } // 对象(数组和函数也是一种对象)
let d: 'hello' = 'hello' // 字面量类型, 表示该变量只能赋值 'hello'

num = undefined // ❌报错: 不能将类型“undefined”分配给类型“number”

数组及其元素的类型:

1
2
3
4
5
let numbers: number[] = [1, 2, 3] // 元素只能是数字类型
let anyNums: any[] = [123, '123', true, null] // 元素可以是任意类型

anyNums[2] = false // false
numbers[0] = 'hello' // ❌报错:不能将类型“string”分配给类型“number”

对象及其属性的类型:

1
2
3
4
5
6
let obj: { name: string, age?: number } // ? 为可选属性, 只能放在必选属性的后面
let obj2: { xxx: number, [key: string]: any } // [key: string]: any 代表任意属性, 并且其值是任意类型

obj = { name: 'Timeic' }
obj2 = { xxx: 0, xx1: '66', xx2: null, xx3: false }
obj = { name: 'Tom', xxx: 'xxx' } // ❌报错:对象字面量只能指定已知属性,并且“xxx”不在类型“{ name: string; age?: number | undefined; }”中

带类型的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 函数参数的类型及参数的默认值
function add(x: number, y: number = 1): number {
return x + y
}

// 函数可选参数(?)
function multiply(a: number, b?: number): number {
return b ? a * b : a;
}

// 先声明函数类型后赋值
const greet: (name: string): string
greet = (x, y) => `Hello, ${name}`

greet('Timeic') // Hello, Timeic
multiply(8, 2) // 16
multiply(6) // 6
add(4) // 5
add(8, '10') // ❌报错: 类型“string”的参数不能赋给类型“number”的参数

void 类型:

1
2
3
4
5
6
7
8
9
10
// void 类型只能赋值为 undefined
function thisVoid(): void {
// 只能返回 undefined, 返回其它值则会报错
return undefined // 通常省略不写, 因为函数默认返回 undefined
}

let v = thisVoid()
console.log(v) // undefined
console.log(v + 123) // ❌报错: 运算符“+”不能应用于类型“void”和“number”(这会导致 NaN 的出现)
// void 类型主要用于指定函数返回值为空, 即没有值, 因此不要对 void 类型的值进行其它操作

never 类型:

1
2
3
4
5
6
7
8
9
10
11
12
13
// never 类型表示永远不会有值的类型

// 抛出异常函数
function throwError(message: string): never {
throw new Error(message) // 函数永远不会 return, 如果函数 return 了则会报错
}

// 无线循环
function infiniteLoop(): never {
while (true) {
// 无限循环,函数永远不会 return, 如果函数 return 了则会报错
}
}

任意类型:

1
2
3
4
5
6
7
8
9
10
11
let a: any // 显示指定 any, 变量 a 可以是任何类型
let b // 隐式 any

a = 'Timeic'
a = null
b = false
b = [1,2,3]

let num: number
// 显示指定 any 类型意味着该变量放弃使用 TS 类型检查功能(可以赋值给任何变量), 开发中不建议使用 any
num = a // num 的值为 null

联合类型( | ):

1
2
3
4
5
6
7
let id: number | '123' // 可以指定多个类型, 不符合这些类型的值则会报错
let arr: (string | number | boolean)[] = ['123', 123, true] // 联合类型的数组

id = "123" // 合法
id = 123 // 合法
arr[2] = false // 合法
id = "456" // ❌报错:不能将类型“"456"”分配给类型“number | "123"”

交叉类型( & ):

1
2
3
4
5
6
// & 代表交叉类型,多个类型必须都符合,否则就会报错
let user: { id: number, name: string } & { isActive: boolean } = {
id: 1,
name: "Alice",
isActive: true
}

类型别名(Type Aliase

类型别名是用来为类型定义一个新的名字。类型别名可以用于为复杂的类型(如联合类型、对象类型)创建简洁的名称,便于复用

语法:

1
type 类型别名 = 类型定义

例:

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
// 定义一个字符串类型的别名
type Name = string

// 定义一个复杂的类型
type User = {
id: number;
name: string;
isActive: boolean;
}

let userName: Name = "Alice"
let newUser: User = {
id: 1,
name: "Alice",
isActive: true,
}

// 为函数定义类型别名
type MathOperation = (a: number, b: number) => number

const add: MathOperation = (x, y) => x + y
const multiply: MathOperation = (x, y) => x * y

// 为联合类型创建别名
type Status = "success" | "error" | "loading"

function printStatus(status: Status) {
console.log(status)
}

printStatus("success")
printStatus("failure") // ❌报错:类型“"failure"”的参数不能赋给类型“Status”的参数

类型推断(Type Inference

TypeScript 的 类型推断 是指编译器根据代码的上下文自动推断出变量、函数返回值等的类型,而无需显式地声明类型。这种机制让代码更加简洁,同时保留类型安全性

基本类型推断:

1
2
3
4
/* TypeScript 会根据赋值自动推断变量的类型 */
let count = 42 // 推断为 number
let message = "Hello, TypeScript" // 推断为 string
let isDone = true // 推断为 boolean
1
2
3
4
/* 未赋初值时的推断 */
let something // 推断为 any
something = 42
something = "hello"

函数的类型推断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// TypeScript 会自动推断返回值类型
function add(a: number, b: number) {
return a + b // 推断返回值为 number
}

// 如果没有手动返回值,则返回值类型被推断为 void
function logMessage(message: string) {
console.log(message) // 推断返回值为 void
}

// 函数参数类型不能推断,对于函数的参数,必须显式声明类型,否则会被推断为 any
function multiply(a: number, b: number) {
return a * b
}

数组的类型推断:

1
2
3
4
5
let numbers = [1, 2, 3]; // 推断为 number[]
let names = ["Alice", "Bob"]; // 推断为 string[]

// 混合类型数组推断为联合类型
let mixed = [1, "hello", true]; // 推断为 (number | string | boolean)[]

对象的类型推断:

1
2
3
4
5
const user = {
id: 1,
name: "Alice",
isActive: true,
}; // 推断为 { id: number; name: string; isActive: boolean }

元组(Tuple

元组(Tuple)是一种特殊的数组( Array )类型,它允许定义一个已知数量和类型的元素的集合。与普通数组不同,元组中的每个元素类型和顺序都是固定的

语法:

1
let tuple: [类型1, 类型2, 类型3, ...] = [值1, 值2, 值3, ...]

例:

1
2
3
4
// 元组类型与元组中的元素会按照位置顺序绑定
let tuple: [number, string] = [25, "Alice"];
tuple[0] = 30
tuple[1] = 30 // ❌报错:不能将类型“number”分配给类型“string”
1
2
3
// 元组的长度是固定的,不允许添加额外元素 (固定长度)
let tuple: [string, number]
tuple = ["Alice", 25, 3] // ❌报错:源具有 3 个元素,但目标仅允许 2 个
1
2
3
4
5
6
7
// tuple 会被 TS 编译器编译为 JS 数组,因此 tuple 可以用数组上所有的属性及方法
let tuple: [string, number] = ["Alice", 25]
tuple.push(10) // 这里不会报错,因此可以修改元组长度,但添加了 tuple 中没有定义的类型会报错

// 只读元组,不允许修改的元组
let tuple: readonly [string, number] = ["Alice", 25]
tuple.push(30) // ❌报错:'push' 方法不能在只读元组上调用

枚举(Enum

枚举( enum )是 TypeScript 中一种用于定义一组具有命名标识符的常量集合的类型。它使我们可以为一组相关的值赋予更有意义的名字,提升代码的可读性和维护性

语法:

1
2
3
4
5
enum 枚举名 {
枚举成员1 = 值1,
枚举成员2 = 值2,
...
}

类型断言(Type Assertion

类型断言是一种显式地告诉 TypeScript 某个值的具体类型,用于编译器无法自动推断出正确类型的情况。类型断言不会在运行时做任何类型检查或转换,仅仅用于在编译时指导 TypeScript

语法:变量 as 类型

例:

1
2
3
// 当你有一个变量类型为 any 时,使用类型断言可以告诉 TypeScript 它的真实类型
let value: any = "Hello"
let vLength: number = (value as string).length

配置文件(tsconfig.json

接口(Interface

泛型()

装饰器()

面向对象(OOP)