TypeScript 快速入门指南

0 阅读6分钟

TypeScript 是 JavaScript 的超集,为 JavaScript 添加了静态类型系统。它让大型项目更易维护,代码更健壮,开发体验更好。

一、TypeScript 是什么?

1.1 核心概念

// JavaScript
let name = "张三"
name = 123  // 可以,但可能导致运行时错误

// TypeScript
let name: string = "张三"
name = 123  // ❌ 编译时报错:不能将类型"number"分配给类型"string"

1.2 为什么选择 TypeScript?

  • 类型安全:编译时发现错误,减少运行时错误
  • 更好的 IDE 支持:智能提示、代码补全、重构
  • 代码可读性:明确的类型注解让代码意图更清晰
  • 渐进式采用:可以在现有 JavaScript 项目中逐步引入

二、快速开始

2.1 安装 TypeScript

# 全局安装
npm install -g typescript

# 或作为项目依赖
npm install --save-dev typescript

# 检查版本
tsc --version

2.2 第一个 TypeScript 程序

// hello.ts
function greet(name: string): string {
  return `Hello, ${name}!`
}

console.log(greet("TypeScript"))

编译并运行:

# 编译
tsc hello.ts

# 运行
node hello.js

TypeScript 快速入门指南

TypeScript 是 JavaScript 的超集,为 JavaScript 添加了静态类型系统。它让大型项目更易维护,代码更健壮,开发体验更好。

一、TypeScript 是什么?

1.1 核心概念

// JavaScript
let name = "张三"
name = 123  // 可以,但可能导致运行时错误

// TypeScript
let name: string = "张三"
name = 123  // ❌ 编译时报错:不能将类型"number"分配给类型"string"

1.2 为什么选择 TypeScript?

  • 类型安全:编译时发现错误,减少运行时错误
  • 更好的 IDE 支持:智能提示、代码补全、重构
  • 代码可读性:明确的类型注解让代码意图更清晰
  • 渐进式采用:可以在现有 JavaScript 项目中逐步引入

二、快速开始

2.1 安装 TypeScript

# 全局安装
npm install -g typescript

# 或作为项目依赖
npm install --save-dev typescript

# 检查版本
tsc --version

2.2 第一个 TypeScript 程序

// hello.ts
function greet(name: string): string {
  return `Hello, ${name}!`
}

console.log(greet("TypeScript"))

编译并运行:

# 编译
tsc hello.ts

# 运行
node hello.js

三、基础类型系统

3.1 基本类型

// 字符串
let name: string = "张三"

// 数字
let age: number = 25
let price: number = 99.99

// 布尔值
let isStudent: boolean = true

// 数组
let numbers: number[] = [1, 2, 3]
let names: Array<string> = ["张三", "李四"]

// 元组
let person: [string, number] = ["张三", 25]

// 枚举
enum Color {
  Red,
  Green,
  Blue
}
let color: Color = Color.Green

// Any(尽量少用)
let anything: any = "可以是任何类型"
anything = 123
anything = true

// Void(无返回值)
function logMessage(): void {
  console.log("This is a message")
}

// Null 和 Undefined
let u: undefined = undefined
let n: null = null

// Never(永远不会返回)
function error(message: string): never {
  throw new Error(message)
}

// Unknown(比 any 更安全)
let notSure: unknown = 4
notSure = "maybe a string"
// notSure.toFixed()  // ❌ 错误:必须先进行类型检查

3.2 类型推断

// TypeScript 会自动推断类型
let message = "Hello"  // 推断为 string
let count = 10         // 推断为 number
let isActive = true    // 推断为 boolean

// 数组类型推断
let arr = [1, 2, 3]    // 推断为 number[]

四、接口和类型别名

4.1 接口(Interface)

// 定义接口
interface User {
  id: number
  name: string
  email?: string  // 可选属性
  readonly createdAt: Date  // 只读属性
}

// 使用接口
const user: User = {
  id: 1,
  name: "张三",
  createdAt: new Date()
}

// user.createdAt = new Date()  // ❌ 错误:只读属性不能修改

// 函数类型接口
interface SearchFunc {
  (source: string, subString: string): boolean
}

const mySearch: SearchFunc = function(src, sub) {
  return src.includes(sub)
}

// 可索引类型
interface StringArray {
  [index: number]: string
}

const arr: StringArray = ["a", "b", "c"]

4.2 类型别名(Type Alias)

// 类型别名
type Point = {
  x: number
  y: number
}

type ID = number | string

type Callback = (data: string) => void

// 使用
const point: Point = { x: 10, y: 20 }
const userId: ID = 123

4.3 接口 vs 类型别名

// 接口可以扩展
interface Animal {
  name: string
}

interface Dog extends Animal {
  breed: string
}

// 类型别名使用交叉类型
type AnimalType = {
  name: string
}

type DogType = AnimalType & {
  breed: string
}

// 接口可以重复声明(合并)
interface Window {
  title: string
}

interface Window {
  width: number
}

// 最终 Window 接口包含 title 和 width

五、函数

5.1 函数类型注解

// 函数声明
function add(x: number, y: number): number {
  return x + y
}

// 函数表达式
const multiply = function(x: number, y: number): number {
  return x * y
}

// 箭头函数
const divide = (x: number, y: number): number => x / y

// 可选参数
function buildName(firstName: string, lastName?: string): string {
  return lastName ? `${firstName} ${lastName}` : firstName
}

// 默认参数
function greet(name: string = "Guest"): string {
  return `Hello, ${name}!`
}

// 剩余参数
function sum(...numbers: number[]): number {
  return numbers.reduce((total, num) => total + num, 0)
}

// 函数重载
function getData(id: number): string
function getData(name: string): string[]
function getData(value: number | string): string | string[] {
  if (typeof value === "number") {
    return `Data for ID: ${value}`
  } else {
    return [`Data for ${value}`, "More data"]
  }
}

六、类与面向对象

6.1 基本类

class Person {
  // 属性
  name: string
  age: number
  
  // 构造函数
  constructor(name: string, age: number) {
    this.name = name
    this.age = age
  }
  
  // 方法
  greet(): string {
    return `Hello, I'm ${this.name}`
  }
}

// 使用
const person = new Person("张三", 25)
console.log(person.greet())

6.2 访问修饰符

class BankAccount {
  // 公共属性(默认)
  public accountNumber: string
  
  // 私有属性(只能在类内部访问)
  private balance: number
  
  // 受保护属性(只能在类和子类中访问)
  protected owner: string
  
  constructor(accountNumber: string, balance: number, owner: string) {
    this.accountNumber = accountNumber
    this.balance = balance
    this.owner = owner
  }
  
  // 公共方法
  public deposit(amount: number): void {
    if (amount > 0) {
      this.balance += amount
    }
  }
  
  public getBalance(): number {
    return this.balance
  }
}

// 简写语法(TypeScript 特有)
class User {
  constructor(
    public id: number,
    private name: string,
    protected email: string
  ) {}
}

6.3 继承

class Animal {
  constructor(public name: string) {}
  
  move(distance: number = 0): void {
    console.log(`${this.name} moved ${distance}m.`)
  }
}

class Dog extends Animal {
  constructor(name: string, public breed: string) {
    super(name)  // 调用父类构造函数
  }
  
  bark(): void {
    console.log("Woof! Woof!")
  }
  
  // 重写父类方法
  move(distance: number = 5): void {
    console.log(`${this.name} runs...`)
    super.move(distance)
  }
}

const dog = new Dog("Buddy", "Golden Retriever")
dog.bark()
dog.move(10)

6.4 抽象类

abstract class Shape {
  constructor(public color: string) {}
  
  // 抽象方法(必须在子类中实现)
  abstract getArea(): number
  
  // 具体方法
  getDescription(): string {
    return `This is a ${this.color} shape`
  }
}

class Circle extends Shape {
  constructor(color: string, public radius: number) {
    super(color)
  }
  
  getArea(): number {
    return Math.PI * this.radius ** 2
  }
}

// const shape = new Shape("red")  // ❌ 错误:不能实例化抽象类
const circle = new Circle("red", 5)
console.log(circle.getArea())

6.5 接口实现

interface Flyable {
  fly(): void
}

interface Swimmable {
  swim(): void
}

class Duck implements Flyable, Swimmable {
  fly(): void {
    console.log("Duck is flying")
  }
  
  swim(): void {
    console.log("Duck is swimming")
  }
}

七、泛型

7.1 基本泛型

// 泛型函数
function identity<T>(arg: T): T {
  return arg
}

// 使用
let output1 = identity<string>("Hello")
let output2 = identity(123)  // 类型推断

// 泛型接口
interface GenericIdentityFn<T> {
  (arg: T): T
}

// 泛型类
class GenericNumber<T> {
  zeroValue: T
  add: (x: T, y: T) => T
  
  constructor(zeroValue: T, add: (x: T, y: T) => T) {
    this.zeroValue = zeroValue
    this.add = add
  }
}

const myNumber = new GenericNumber<number>(0, (x, y) => x + y)

7.2 泛型约束

interface Lengthwise {
  length: number
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length)
  return arg
}

// loggingIdentity(3)  // ❌ 错误:数字没有length属性
loggingIdentity("hello")  // ✅
loggingIdentity([1, 2, 3])  // ✅

八、高级类型

8.1 联合类型和交叉类型

// 联合类型:可以是多种类型之一
type ID = number | string

// 交叉类型:同时具有多种类型的特性
interface Named {
  name: string
}

interface Aged {
  age: number
}

type Person = Named & Aged

const person: Person = {
  name: "张三",
  age: 25
}

8.2 类型守卫

// typeof 类型守卫
function padLeft(value: string, padding: string | number): string {
  if (typeof padding === "number") {
    return " ".repeat(padding) + value
  }
  return padding + value
}

// instanceof 类型守卫
class Bird {
  fly() {
    console.log("flying")
  }
}

class Fish {
  swim() {
    console.log("swimming")
  }
}

function move(pet: Bird | Fish) {
  if (pet instanceof Bird) {
    pet.fly()
  } else {
    pet.swim()
  }
}

// 自定义类型守卫
interface Cat {
  meow(): void
}

interface Dog {
  bark(): void
}

function isDog(pet: Cat | Dog): pet is Dog {
  return (pet as Dog).bark !== undefined
}

function makeSound(pet: Cat | Dog) {
  if (isDog(pet)) {
    pet.bark()
  } else {
    pet.meow()
  }
}

九、模块系统

9.1 导出和导入

// math.ts - 导出
export const PI = 3.14159

export function add(x: number, y: number): number {
  return x + y
}

export class Calculator {
  multiply(x: number, y: number): number {
    return x * y
  }
}

// 默认导出
export default class AdvancedCalculator {
  // ...
}

// main.ts - 导入
import { PI, add, Calculator } from './math'
import AdvancedCalculator from './math'

console.log(PI)
console.log(add(2, 3))

// 重命名导入
import { Calculator as Calc } from './math'

// 导入所有
import * as MathUtils from './math'
console.log(MathUtils.PI)

十、TypeScript 配置

10.1 tsconfig.json

{
  "compilerOptions": {
    // 基本配置
    "target": "ES2020",           // 编译目标版本
    "module": "commonjs",         // 模块系统
    "lib": ["ES2020", "DOM"],     // 包含的库文件
    
    // 输出配置
    "outDir": "./dist",           // 输出目录
    "rootDir": "./src",           // 源代码目录
    
    // 类型检查配置
    "strict": true,               // 启用所有严格类型检查
    "noImplicitAny": true,        // 禁止隐式 any
    "strictNullChecks": true,     // 严格的 null 检查
    
    // 模块解析
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "@/*": ["src/*"]
    },
    
    // 其他
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

10.2 常用编译选项

# 编译整个项目
tsc

# 编译单个文件
tsc hello.ts

# 监听模式(自动重新编译)
tsc --watch

# 生成声明文件
tsc --declaration

# 指定配置文件
tsc --project tsconfig.prod.json

十一、实际应用示例

11.1 简单的 Todo 应用

// types/todo.ts
export interface Todo {
  id: number
  title: string
  completed: boolean
  createdAt: Date
}

export type FilterType = 'all' | 'active' | 'completed'

// services/todoService.ts
import { Todo } from '../types/todo'

class TodoService {
  private todos: Todo[] = []
  private nextId = 1
  
  addTodo(title: string): Todo {
    const todo: Todo = {
      id: this.nextId++,
      title,
      completed: false,
      createdAt: new Date()
    }
    this.todos.push(todo)
    return todo
  }
  
  getTodos(filter: FilterType = 'all'): Todo[] {
    switch (filter) {
      case 'active':
        return this.todos.filter(todo => !todo.completed)
      case 'completed':
        return this.todos.filter(todo => todo.completed)
      default:
        return [...this.todos]
    }
  }
  
  toggleTodo(id: number): Todo | null {
    const todo = this.todos.find(t => t.id === id)
    if (todo) {
      todo.completed = !todo.completed
      return todo
    }
    return null
  }
}

export const todoService = new TodoService()

// main.ts
import { todoService } from './services/todoService'
import type { FilterType } from './types/todo'

// 添加任务
todoService.addTodo("学习 TypeScript")
todoService.addTodo("完成项目")

// 切换任务状态
todoService.toggleTodo(1)

// 获取任务列表
const activeTodos = todoService.getTodos('active')
console.log(activeTodos)

11.2 API 请求封装

// types/api.ts
export interface ApiResponse<T> {
  data: T
  status: number
  message: string
}

export interface User {
  id: number
  name: string
  email: string
}

// utils/api.ts
import type { ApiResponse } from '../types/api'

class ApiClient {
  private baseURL: string
  
  constructor(baseURL: string) {
    this.baseURL = baseURL
  }
  
  async get<T>(endpoint: string): Promise<ApiResponse<T>> {
    const response = await fetch(`${this.baseURL}${endpoint}`)
    return response.json()
  }
  
  async post<T>(endpoint: string, data: any): Promise<ApiResponse<T>> {
    const response = await fetch(`${this.baseURL}${endpoint}`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(data)
    })
    return response.json()
  }
}

export const apiClient = new ApiClient('https://api.example.com')

// 使用
const fetchUsers = async () => {
  try {
    const response = await apiClient.get<User[]>('/users')
    console.log(response.data)
  } catch (error) {
    console.error('获取用户失败:', error)
  }
}

十二、常见问题解决

12.1 第三方库类型声明

# 安装类型声明文件
npm install --save-dev @types/lodash
npm install --save-dev @types/react
npm install --save-dev @types/node

12.2 缺少类型声明

// 声明模块
declare module 'untyped-module' {
  export function someFunction(): void
}

// 全局声明
declare global {
  interface Window {
    myCustomProperty: string
  }
}

十三、学习资源

13.1 官方资源

  • TypeScript 官网
  • TypeScript Playground
  • TypeScript Handbook

13.2 推荐学习路径

  1. 基础:类型系统、接口、类
  2. 进阶:泛型、高级类型、装饰器
  3. 实战:配置项目、集成框架、工具链

13.3 练习建议

  1. 将现有 JavaScript 项目迁移到 TypeScript
  2. 使用 TypeScript 重写小型工具库
  3. 参与开源 TypeScript 项目

十四、总结

TypeScript 的核心价值:

  1. 类型安全:减少运行时错误
  2. 开发体验:更好的 IDE 支持
  3. 代码维护:清晰的类型注解
  4. 团队协作:明确的接口约定