Typescript基础
TypeScript 基础语法和核心概念
一、TS 概念理论
TS 基础铺垫
1. 什么是 TS
TypeScript(TS) 是由微软开发的 JavaScript 的超集,在 JS 的基础上添加了静态类型系统,最终会被编译为纯 JavaScript 运行。
- 超集关系: JS ⊂ TS,所有合法的 JS 代码都是合法的 TS 代码
- 静态类型: 在编译阶段就能发现类型错误,而不是运行时
- 强大的 IDE 支持: 代码补全、类型推断、重构更安全
💡 提示: TS 不能直接在浏览器或 Node.js 中运行,需要先通过
tsc编译为 JS。
| 对比项 | JavaScript | TypeScript |
|---|---|---|
| 类型系统 | 动态类型 | 静态类型 |
| 错误发现时机 | 运行时 | 编译时 |
| 代码可维护性 | 一般 | 强 |
| 学习成本 | 低 | 中等 |
| 适用场景 | 小型项目 | 中大型项目 |
2. TS 基础类型与写法
TS 中类型注解语法为 变量名: 类型:
// 基础类型
let isDone: boolean = false // 布尔值
let count: number = 42 // 数字(整数/浮点数统一为 number)
let name: string = 'Shea' // 字符串
let nothing: null = null // null
let undef: undefined = undefined // undefined
let sym: symbol = Symbol('id') // symbol
let big: bigint = 100n // bigint(ES2020+)
// 特殊类型
let anyVal: any = '任意类型' // any:关闭类型检查(慎用!)
let unknownVal: unknown = 42 // unknown:比 any 更安全的宽泛类型
let noReturn: void = undefined // void:函数无返回值
function throwErr(): never { // never:永不返回(抛出异常/死循环)
throw new Error('error')
}
⚠️ 注意:
any会绕过所有类型检查,大量使用会让 TS 失去意义,尽量用unknown替代。
3. TS 断言初步
断言是告诉编译器「我比你更了解这个值的类型」,分为三种:
i. 类型断言(Type Assertion)
// 写法一:尖括号语法(不推荐在 JSX 中使用)
let val: unknown = 'hello'
let len1: number = (<string>val).length
// 写法二:as 语法(推荐)
let len2: number = (val as string).length
// 双重断言:先断言为 unknown,再断言为目标类型(慎用)
let num = (someValue as unknown) as number
ii. 非空断言(Non-null Assertion)
使用 ! 告诉编译器该值不为 null 或 undefined:
function processInput(input: string | null) {
// 不加 ! 会报错:Object is possibly 'null'
console.log(input!.toUpperCase()) // 断言 input 一定不为 null
}
// DOM 操作中常见
const button = document.getElementById('btn')! // 断言元素一定存在
button.addEventListener('click', () => {})
🚨 警告: 非空断言只是告诉编译器不报错,若运行时确实为 null,仍会抛出异常。
iii. 赋值断言(Definite Assignment Assertion)
用于类属性,告诉编译器属性一定会在使用前被赋值:
class MyClass {
// 使用 ! 告诉 TS:我保证 value 在使用前会被赋值
value!: string
init() {
this.value = 'initialized'
}
}
TS 类型认知
4. 联合类型 / 交叉类型
| 类型 | 符号 | 含义 | 比喻 |
|---|---|---|---|
| 联合类型 | | |
A 或 B,满足其中一个即可 | 并集(A ∪ B) |
| 交叉类型 | & |
A 且 B,必须同时满足 | 交集(A ∩ B)合并 |
// ─── 联合类型(Union Types)───
type StringOrNumber = string | number
let id: StringOrNumber = 'abc' // ✅
id = 123 // ✅
// id = true // ❌ 不允许
// 函数参数联合类型
function formatInput(val: string | number): string {
// 使用前需要收窄类型(类型守卫)
if (typeof val === 'string') {
return val.toUpperCase()
}
return val.toFixed(2)
}
// ─── 交叉类型(Intersection Types)───
type Person = { name: string; age: number }
type Employee = { company: string; role: string }
// 合并两个类型,必须同时具备所有属性
type Staff = Person & Employee
const staff: Staff = {
name: 'Shea',
age: 25,
company: 'Acme',
role: 'Frontend Dev'
}
5. 类型守卫(Type Guards)
类型守卫是在运行时收窄(narrow)类型范围的机制,让 TS 在某个代码块中知道更具体的类型。
i. in 操作符
type Cat = { meow: () => void }
type Dog = { bark: () => void }
function makeSound(animal: Cat | Dog) {
if ('meow' in animal) {
// 此处 TS 知道 animal 是 Cat
animal.meow()
} else {
// 此处 TS 知道 animal 是 Dog
animal.bark()
}
}
ii. typeof 操作符
function double(val: string | number) {
if (typeof val === 'string') {
// 此处 val 被收窄为 string
return val.repeat(2)
}
// 此处 val 被收窄为 number
return val * 2
}
💡 提示:
typeof守卫适用于基础类型:string、number、boolean、symbol、bigint、undefined、function。
iii. instanceof 操作符
class HttpError extends Error {
constructor(public statusCode: number, message: string) {
super(message)
}
}
function handleError(error: Error) {
if (error instanceof HttpError) {
// 此处 error 被收窄为 HttpError
console.log(error.statusCode)
} else {
console.log(error.message)
}
}
iv. 自定义类型守卫(Type Predicates)
使用 参数 is 类型 语法定义返回类型谓词的函数:
type Fish = { swim: () => void }
type Bird = { fly: () => void }
// 返回类型 `pet is Fish` 就是类型谓词
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim() // TS 知道这里是 Fish
} else {
pet.fly() // TS 知道这里是 Bird
}
}
3 TS 基础类型语法
6. 数组、对象、函数
// ─── 数组 ───
let nums: number[] = [1, 2, 3] // 写法一
let strs: Array<string> = ['a', 'b'] // 写法二(泛型写法)
let tuple: [string, number] = ['age', 25] // 元组:固定长度和类型
// ─── 对象 ───
// 使用对象字面量类型
let user: { name: string; age: number; email?: string } = {
name: 'Shea',
age: 25
// email 是可选属性,可以不写
}
// 索引签名:动态 key
let dict: { [key: string]: number } = {
apple: 1,
banana: 2
}
// ─── 函数 ───
// 参数类型 + 返回值类型
function add(a: number, b: number): number {
return a + b
}
// 箭头函数类型
const greet = (name: string): string => `Hello, ${name}!`
// 可选参数(必须放在必填参数后面)
function buildName(firstName: string, lastName?: string): string {
return lastName ? `${firstName} ${lastName}` : firstName
}
// 默认参数
function createUser(name: string, role: string = 'user') {
return { name, role }
}
// 剩余参数
function sum(...nums: number[]): number {
return nums.reduce((acc, n) => acc + n, 0)
}
// 函数重载
function format(x: string): string
function format(x: number): string
function format(x: string | number): string {
return String(x)
}
7. 接口(Interface)
接口(Interface)用于定义对象的结构契约,是 TS 中描述对象形状的核心方式。
// 基础接口定义
interface User {
id: number
name: string
age?: number // 可选属性
readonly email: string // 只读属性,赋值后不可修改
}
// 接口继承(支持多继承)
interface Admin extends User {
permissions: string[]
}
// 接口合并(同名接口自动合并)
interface Window {
myProp: string
}
interface Window {
myMethod(): void
}
// 合并结果:Window 同时具有 myProp 和 myMethod
// 函数类型接口
interface SearchFunc {
(source: string, subString: string): boolean
}
const search: SearchFunc = (src, sub) => src.includes(sub)
// 索引接口
interface StringArray {
[index: number]: string
}
| 对比项 | interface |
type |
|---|---|---|
| 描述对象/类 | ✅ 更语义化 | ✅ 可以 |
| 描述基础类型/联合/交叉 | ❌ 不支持 | ✅ 支持 |
| 同名自动合并 | ✅ 支持 | ❌ 报错 |
extends 继承 |
✅ 支持 | ✅ 可用 & |
| 推荐使用场景 | 对象结构、类实现 | 复杂类型别名 |
8. 类(Class)
TS 对 ES6 类进行了增强,添加了访问修饰符、抽象类等特性:
// 访问修饰符
class Person {
public name: string // 公开(默认),外部可访问
private age: number // 私有,只能在类内部访问
protected id: number // 受保护,可在类内部和子类访问
readonly birth: Date // 只读
// 构造函数参数简写(自动声明并赋值属性)
constructor(
public username: string,
private password: string
) {
this.name = username
this.age = 0
this.id = Math.random()
this.birth = new Date()
}
// getter / setter
get displayName(): string {
return `[${this.id}] ${this.name}`
}
set displayName(val: string) {
this.name = val
}
}
// 类实现接口
interface Printable {
print(): void
}
class Document implements Printable {
print() {
console.log('Printing...')
}
}
// 抽象类:不能直接实例化,只能被继承
abstract class Shape {
abstract getArea(): number // 子类必须实现
toString() {
return `Area: ${this.getArea()}`
}
}
class Circle extends Shape {
constructor(private radius: number) {
super()
}
getArea(): number {
return Math.PI * this.radius ** 2
}
}
TS 进阶
10. 泛型(Generics)
泛型是 TS 最强大的特性之一,让函数、类、接口能够处理多种类型而不丢失类型信息。
// ─── 泛型函数 ───
// T 是类型参数,调用时由 TS 自动推断或手动传入
function identity<T>(arg: T): T {
return arg
}
identity<string>('hello') // 手动指定
identity(42) // 自动推断为 number
// 泛型数组
function getFirst<T>(arr: T[]): T {
return arr[0]
}
// ─── 泛型约束(extends)───
interface Lengthwise {
length: number
}
// 约束 T 必须具有 length 属性
function logLength<T extends Lengthwise>(arg: T): number {
return arg.length
}
logLength('hello') // ✅ string 有 length
logLength([1, 2]) // ✅ array 有 length
// logLength(42) // ❌ number 没有 length
// ─── 泛型接口 ───
interface ApiResponse<T> {
code: number
message: string
data: T
}
type UserResponse = ApiResponse<{ name: string; age: number }>
// ─── 泛型类 ───
class Stack<T> {
private items: T[] = []
push(item: T): void {
this.items.push(item)
}
pop(): T | undefined {
return this.items.pop()
}
}
const stack = new Stack<number>()
stack.push(1)
// ─── 常用内置泛型工具类型 ───
type Partial<T> = { [P in keyof T]?: T[P] } // 所有属性变可选
type Required<T> = { [P in keyof T]-?: T[P] } // 所有属性变必填
type Readonly<T> = { readonly [P in keyof T]: T[P] }// 所有属性变只读
type Pick<T, K extends keyof T> = { [P in K]: T[P] }// 选取部分属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>> // 排除属性
type Record<K extends keyof any, T> = { [P in K]: T } // 键值映射
// 使用示例
interface Todo {
title: string
description: string
completed: boolean
}
type TodoPreview = Pick<Todo, 'title' | 'completed'>
type PartialTodo = Partial<Todo>
11. 装饰器(Decorators)
装饰器是一种元编程特性,本质是一个函数,用于修改类、方法、属性等的行为。
⚠️ 注意: 使用装饰器需要在
tsconfig.json中开启"experimentalDecorators": true。
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
// ─── 类装饰器 ───
// 接收构造函数作为参数
function sealed(constructor: Function) {
Object.seal(constructor) // 密封类本身
Object.seal(constructor.prototype) // 密封原型
}
@sealed
class BugReport {
type = 'report'
title: string
constructor(t: string) {
this.title = t
}
}
// ─── 方法装饰器 ───
function log(target: any, key: string, descriptor: PropertyDescriptor) {
const original = descriptor.value
// 替换原始方法,添加日志
descriptor.value = function (...args: any[]) {
console.log(`调用 ${key},参数:`, args)
const result = original.apply(this, args)
console.log(`${key} 返回:`, result)
return result
}
return descriptor
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b
}
}
// ─── 属性装饰器 ───
function readonly(target: any, key: string) {
Object.defineProperty(target, key, {
writable: false
})
}
class Point {
@readonly
PI = 3.14159
}
// ─── 参数装饰器 ───
function required(target: any, key: string, index: number) {
console.log(`${key} 的第 ${index} 个参数是必须的`)
}
class Service {
greet(@required name: string) {
return `Hello, ${name}`
}
}
| 装饰器类型 | 参数 | 使用位置 |
|---|---|---|
| 类装饰器 | constructor |
@Dec class Foo {} |
| 方法装饰器 | target, key, descriptor |
@Dec method() {} |
| 属性装饰器 | target, key |
@Dec prop: type |
| 参数装饰器 | target, key, paramIndex |
method(@Dec param) |
| 访问器装饰器 | target, key, descriptor |
@Dec get/set prop |
二、TS 编译运行机制
编译流程
TS 源码 (.ts)
↓ tsc 编译器
↓ 1. 词法分析 → Token 流
↓ 2. 语法分析 → AST(抽象语法树)
↓ 3. 类型检查(Binder + Checker)
↓ 4. 代码生成(Emitter)
JS 代码 (.js)
tsconfig.json 常用配置
{
"compilerOptions": {
"target": "ES2020", // 编译目标版本
"module": "CommonJS", // 模块系统
"strict": true, // 开启所有严格检查
"noImplicitAny": true, // 禁止隐式 any
"strictNullChecks": true, // 开启 null/undefined 检查
"outDir": "./dist", // 编译输出目录
"rootDir": "./src", // 源码根目录
"declaration": true, // 生成 .d.ts 类型声明文件
"sourceMap": true, // 生成 source map
"paths": { // 路径别名
"@/*": ["./src/*"]
}
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
TS 在项目中的运行方式
| 方式 | 工具 | 适用场景 |
|---|---|---|
| 预编译 | tsc |
生产环境打包 |
| 实时编译 | ts-node |
Node.js 开发/脚本 |
| 构建工具集成 | Vite / Webpack + ts-loader |
前端工程(React/Vue) |
| 按需转译 | esbuild / swc |
高性能构建(不做类型检查) |
💡 提示: 在 Vite 项目中,TS 由
esbuild负责转译(不做类型检查),类型检查由vue-tsc或tsc --noEmit单独运行。
参考资料
- 📖 TS Handbook 中文版
- 📖 TypeScript 官方文档
- 🛝 TypeScript Playground(在线练习)