Skip to content

TypeScript 入门教程

TypeScript 是 JavaScript 的超集,为 JavaScript 添加了静态类型定义。本教程将帮助您从零开始学习 TypeScript。

什么是 TypeScript?

TypeScript 是由 Microsoft 开发的开源编程语言,它在 JavaScript 的基础上添加了静态类型定义。TypeScript 代码会被编译成纯 JavaScript 代码,可以在任何支持 JavaScript 的环境中运行。

主要优势

  • 🔍 静态类型检查 - 在编译时发现错误
  • 🛠️ 更好的 IDE 支持 - 智能提示、重构、导航
  • 📚 更好的代码文档 - 类型即文档
  • 🔧 更容易重构 - 类型安全的重构
  • 🚀 渐进式采用 - 可以逐步迁移现有 JavaScript 项目

环境搭建

安装 TypeScript

bash
# 全局安装
npm install -g typescript

# 项目内安装
npm install -D typescript

# 检查版本
tsc --version

初始化项目

bash
# 创建 tsconfig.json
tsc --init

# 或者手动创建
mkdir my-ts-project
cd my-ts-project
npm init -y
npm install -D typescript @types/node

基本配置

json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

基础类型

基本类型

typescript
// 布尔值
let isDone: boolean = false

// 数字
let decimal: number = 6
let hex: number = 0xf00d
let binary: number = 0b1010
let octal: number = 0o744

// 字符串
let color: string = "blue"
let fullName: string = `Bob Bobbington`
let age: number = 37
let sentence: string = `Hello, my name is ${fullName}.`

// 数组
let list: number[] = [1, 2, 3]
let list2: Array<number> = [1, 2, 3]

// 元组
let x: [string, number]
x = ["hello", 10] // OK

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

// Any
let notSure: any = 4
notSure = "maybe a string instead"
notSure = false

// Void
function warnUser(): void {
  console.log("This is my warning message")
}

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

联合类型

typescript
// 联合类型
let value: string | number
value = "hello" // OK
value = 42 // OK

// 类型守卫
function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value
  }
  if (typeof padding === "string") {
    return padding + value
  }
  throw new Error(`Expected string or number, got '${padding}'.`)
}

接口和类型

接口定义

typescript
// 基本接口
interface User {
  name: string
  age: number
  email?: string // 可选属性
  readonly id: number // 只读属性
}

const user: User = {
  id: 1,
  name: "张三",
  age: 25,
  email: "zhangsan@example.com"
}

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

let mySearch: SearchFunc = function (source: string, subString: string): boolean {
  let result = source.search(subString)
  return result > -1
}

类型别名

typescript
// 基本类型别名
type Name = string
type NameResolver = () => string
type NameOrResolver = Name | NameResolver

function getName(n: NameOrResolver): Name {
  if (typeof n === "string") {
    return n
  } else {
    return n()
  }
}

// 泛型类型别名
type Container<T> = { value: T }

函数

函数类型

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

// 函数表达式
let myAdd = function (x: number, y: number): number {
  return x + y
}

// 箭头函数
let myAdd2 = (x: number, y: number): number => x + y

// 可选参数
function buildName(firstName: string, lastName?: string) {
  if (lastName) return firstName + " " + lastName
  else return firstName
}

// 默认参数
function buildName2(firstName: string, lastName = "Smith") {
  return firstName + " " + lastName
}

// 剩余参数
function buildName3(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ")
}

基本类

typescript
class Animal {
  private name: string
  protected age: number
  public species: string

  constructor(name: string, age: number, species: string) {
    this.name = name
    this.age = age
    this.species = species
  }

  public move(distanceInMeters: number = 0) {
    console.log(`${this.name} moved ${distanceInMeters}m.`)
  }

  protected getAge(): number {
    return this.age
  }
}

class Dog extends Animal {
  constructor(name: string, age: number) {
    super(name, age, "Dog")
  }

  public bark() {
    console.log("Woof! Woof!")
  }

  public getAgeInDogYears(): number {
    return this.getAge() * 7
  }
}

泛型

基本泛型

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

let output = identity<string>("myString")
let output2 = identity("myString") // 类型推断

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

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

  constructor(zeroValue: T, addFn: (x: T, y: T) => T) {
    this.zeroValue = zeroValue
    this.add = addFn
  }
}

let myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y)

泛型约束

typescript
interface Lengthwise {
  length: number
}

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

loggingIdentity({ length: 10, value: 3 }) // OK

高级类型

映射类型

typescript
// Partial - 将所有属性变为可选
type Partial<T> = {
  [P in keyof T]?: T[P]
}

interface User {
  name: string
  age: number
  email: string
}

type PartialUser = Partial<User>

// Pick - 选择部分属性
type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}

type UserNameAndAge = Pick<User, 'name' | 'age'>

// Omit - 排除部分属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

type UserWithoutEmail = Omit<User, 'email'>

条件类型

typescript
// 基本条件类型
type TypeName<T> = T extends string
  ? "string"
  : T extends number
  ? "number"
  : T extends boolean
  ? "boolean"
  : "object"

type T0 = TypeName<string> // "string"
type T1 = TypeName<true> // "boolean"

实用工具类型

typescript
interface User {
  id: number
  name: string
  email: string
  age: number
}

// Partial<T> - 所有属性可选
type PartialUser = Partial<User>

// Required<T> - 所有属性必需
type RequiredUser = Required<PartialUser>

// Readonly<T> - 所有属性只读
type ReadonlyUser = Readonly<User>

// Record<K, T> - 创建对象类型
type UserRoles = Record<'admin' | 'user' | 'guest', User>

// ReturnType<T> - 获取函数返回类型
type T3 = ReturnType<() => string> // string

// Parameters<T> - 获取函数参数类型
type T4 = Parameters<(a: string, b: number) => void> // [string, number]

最佳实践

严格模式配置

json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true
  }
}

类型断言的正确使用

typescript
// 避免使用 any
let someValue: unknown = "this is a string"

// 使用类型断言
let strLength: number = (someValue as string).length

// 或者使用类型守卫
function isString(value: unknown): value is string {
  return typeof value === 'string'
}

if (isString(someValue)) {
  let strLength: number = someValue.length
}

优先使用接口

typescript
// 推荐:使用接口
interface User {
  name: string
  age: number
}

// 接口可以扩展
interface AdminUser extends User {
  permissions: string[]
}

// 类型别名适用于联合类型
type Status = 'loading' | 'success' | 'error'
type ID = string | number

常见问题

什么时候使用 interface,什么时候使用 type?

  • 定义对象结构时优先使用 interface
  • 定义联合类型、原始类型别名时使用 type
  • 需要使用映射类型、条件类型时使用 type

如何处理第三方库的类型?

bash
# 安装类型定义
npm install --save-dev @types/lodash
typescript
// 或者自己声明
declare module 'some-library' {
  export function someFunction(): void
}

如何在现有项目中引入 TypeScript?

  1. 安装 TypeScript 和相关依赖
  2. 创建 tsconfig.json 配置文件
  3. .js 文件逐步重命名为 .ts
  4. 添加类型注解
  5. 解决类型错误

总结

TypeScript 为 JavaScript 开发带来了类型安全和更好的开发体验。通过掌握基础类型、接口、泛型等核心概念,您可以编写更可靠、更易维护的代码。

建议从小项目开始实践,逐步熟悉 TypeScript 的各种特性,最终能够在大型项目中充分发挥 TypeScript 的优势。


继续学习:

vitepress开发指南