TypeScript基础

TypeScript 是 JavaScript 的超集,添加了静态类型系统。它帮助你在开发阶段发现错误,提升代码质量和开发体验。

TypeScript 简介

为什么选择 TypeScript

优势 说明
类型安全 编译时发现类型错误
智能提示 IDE 更好的代码补全
代码文档 类型即文档
重构友好 安全地重构代码
团队协作 接口契约更清晰

安装与配置

# 安装 TypeScript
npm install -D typescript

# 初始化配置
npx tsc --init

# 编译文件
npx tsc

# 监听模式
npx tsc --watch
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

基础类型

原始类型

// 布尔值
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 name: string = `东巴文`
let sentence: string = `Hello, ${name}`

// 空值
function log(msg: string): void {
  console.log(msg)
}

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

// 大整数
let big: bigint = 100n

// Symbol
let sym: symbol = Symbol('key')

数组

// 方式一:元素类型[]
let list: number[] = [1, 2, 3]
let names: string[] = ['东巴文', '用户A']

// 方式二:泛型数组
let list2: Array<number> = [1, 2, 3]

// 只读数组
let readonlyList: readonly number[] = [1, 2, 3]
let readonlyList2: ReadonlyArray<number> = [1, 2, 3]

// 多维数组
let matrix: number[][] = [[1, 2], [3, 4]]

元组

// 固定长度和类型的数组
let tuple: [string, number] = ['东巴文', 18]

// 访问元素
console.log(tuple[0])  // 东巴文
console.log(tuple[1])  // 18

// 可选元素
let optionalTuple: [string, number?] = ['东巴文']

// 剩余元素
let restTuple: [number, ...string[]] = [1, 'a', 'b', 'c']

// 只读元组
let readonlyTuple: readonly [string, number] = ['东巴文', 18]

对象

// 对象类型
let obj: object = { name: '东巴文' }

// 具体属性
let user: { name: string; age: number } = {
  name: '东巴文',
  age: 18
}

// 可选属性
let config: { host: string; port?: number } = {
  host: 'localhost'
}

// 索引签名
let dict: { [key: string]: number } = {
  a: 1,
  b: 2
}

any 和 unknown

// any:任意类型,放弃类型检查
let anything: any = 'hello'
anything = 123
anything = { name: '东巴文' }
anything.doSomething()  // 不报错,但可能运行时错误

// unknown:安全的 any
let uncertain: unknown = 'hello'
uncertain = 123

// 使用前需要类型检查
if (typeof uncertain === 'string') {
  console.log(uncertain.toUpperCase())
}

// 类型断言
let value: unknown = '东巴文'
let length: number = (value as string).length
let length2: number = (<string>value).length

never 和 void

// void:没有返回值
function logMessage(msg: string): void {
  console.log(msg)
}

// never:永不存在的值
function error(message: string): never {
  throw new Error(message)
}

function infiniteLoop(): never {
  while (true) {}
}

// 联合类型中的 never
type NonNullable<T> = T extends null | undefined ? never : T

接口

基本接口

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

let user: User = {
  id: 1,
  name: '东巴文',
  email: 'dongba@example.com'
}

user.name = '新名字'
// user.email = 'new@example.com'  // 错误:只读属性

函数类型

interface SearchFunc {
  (source: string, subString: string): boolean
}

let search: SearchFunc = (src, sub) => {
  return src.search(sub) !== -1
}

可索引类型

interface StringArray {
  [index: number]: string
}

let arr: StringArray = ['东巴文', '用户A']

interface StringDictionary {
  [key: string]: string | number
  length: number
  name: string
}

类类型

interface ClockInterface {
  currentTime: Date
  setTime(d: Date): void
}

class Clock implements ClockInterface {
  currentTime: Date = new Date()
  
  setTime(d: Date) {
    this.currentTime = d
  }
  
  constructor(h: number, m: number) {}
}

接口继承

interface Animal {
  name: string
}

interface Dog extends Animal {
  breed: string
}

interface Cat extends Animal {
  meow(): void
}

// 多继承
interface Pet extends Dog, Cat {
  owner: string
}

let pet: Pet = {
  name: '小黄',
  breed: '金毛',
  owner: '东巴文',
  meow() { console.log('喵') }
}

合并声明

interface Box {
  height: number
  width: number
}

interface Box {
  depth: number
}

let box: Box = {
  height: 10,
  width: 20,
  depth: 30
}

类型别名

基本用法

type Name = string
type Age = number
type User = {
  name: Name
  age: Age
}

// 联合类型
type ID = string | number
type Status = 'pending' | 'approved' | 'rejected'

// 交叉类型
type Employee = User & {
  employeeId: string
  department: string
}

// 原始类型
type Point = [number, number]
type Callback = (data: unknown) => void

interface vs type

// interface:可扩展、可合并
interface User {
  name: string
}
interface User {
  age: number
}

// type:更灵活,支持联合、交叉、映射等
type ID = string | number
type PartialUser = Partial<User>
type ReadonlyUser = Readonly<User>

// 相同点:都可以描述对象
interface IUser {
  name: string
}

type TUser = {
  name: string
}

函数

函数类型

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

// 函数表达式
const subtract = (a: number, b: number): number => a - b

// 类型别名
type MathFunc = (a: number, b: number) => number

const multiply: MathFunc = (a, b) => a * b
const divide: MathFunc = (a, b) => a / b

可选参数和默认参数

// 可选参数
function greet(name: string, greeting?: string): string {
  return greeting ? `${greeting}, ${name}` : `Hello, ${name}`
}

greet('东巴文')           // Hello, 东巴文
greet('东巴文', 'Hi')    // Hi, 东巴文

// 默认参数
function greetWithDefault(name: string, greeting: string = 'Hello'): string {
  return `${greeting}, ${name}`
}

greetWithDefault('东巴文')  // Hello, 东巴文

剩余参数

function sum(...numbers: number[]): number {
  return numbers.reduce((total, n) => total + n, 0)
}

sum(1, 2, 3, 4, 5)  // 15

函数重载

// 重载签名
function format(input: string): string
function format(input: number): string
function format(input: string | number): string {
  if (typeof input === 'string') {
    return `String: ${input}`
  }
  return `Number: ${input}`
}

format('东巴文')  // String: 东巴文
format(123)      // Number: 123

泛型

基本泛型

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

let output1 = identity<string>('东巴文')
let output2 = identity(123)  // 类型推断

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

let myIdentity: GenericIdentity<number> = identity

// 泛型类
class GenericBox<T> {
  private value: T
  
  constructor(value: T) {
    this.value = value
  }
  
  getValue(): T {
    return this.value
  }
}

let box = new GenericBox<string>('东巴文')

泛型约束

// 约束必须有 length 属性
interface Lengthwise {
  length: number
}

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

logLength('hello')     // 5
logLength([1, 2, 3])   // 3
// logLength(123)      // 错误

// 多类型参数约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

const user = { name: '东巴文', age: 18 }
getProperty(user, 'name')  // 东巴文
// getProperty(user, 'email')  // 错误

泛型工具类型

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

// Required:所有属性必需
type RequiredUser = Required<User>

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

// Pick:选取部分属性
type UserName = Pick<User, 'name'>

// Omit:排除部分属性
type UserWithoutId = Omit<User, 'id'>

// Record:构造对象类型
type UserMap = Record<string, User>

// ReturnType:获取函数返回类型
type R = ReturnType<() => string>  // string

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

// NonNullable:排除 null 和 undefined
type NonNull = NonNullable<string | null | undefined>  // string

// Extract:从联合类型中提取
type Extracted = Extract<'a' | 'b' | 'c', 'a' | 'b'>  // 'a' | 'b'

// Exclude:从联合类型中排除
type Excluded = Exclude<'a' | 'b' | 'c', 'a' | 'b'>  // 'c'

类型断言

// 尖括号语法
let value: any = '东巴文'
let length: number = (<string>value).length

// as 语法(推荐)
let length2: number = (value as string).length

// 非空断言
let element = document.getElementById('app')!
element.innerHTML = '东巴文'

// 双重断言(不推荐)
let x = 'hello' as any as number

类型守卫

// typeof
function process(value: string | number) {
  if (typeof value === 'string') {
    return value.toUpperCase()
  }
  return value.toFixed(2)
}

// instanceof
class Dog {
  bark() {}
}

class Cat {
  meow() {}
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark()
  } else {
    animal.meow()
  }
}

// in 操作符
interface Bird {
  fly(): void
}

interface Fish {
  swim(): void
}

function move(animal: Bird | Fish) {
  if ('fly' in animal) {
    animal.fly()
  } else {
    animal.swim()
  }
}

// 自定义类型守卫
function isFish(animal: Bird | Fish): animal is Fish {
  return 'swim' in animal
}

function move2(animal: Bird | Fish) {
  if (isFish(animal)) {
    animal.swim()
  } else {
    animal.fly()
  }
}

声明文件

// 声明全局变量
declare const API_URL: string
declare function log(msg: string): void

// 声明模块
declare module 'my-library' {
  export function doSomething(): void
  export const version: string
}

// 声明文件(.d.ts)
// types/my-library/index.d.ts
declare module 'my-library' {
  interface Options {
    debug?: boolean
  }
  
  export function init(options?: Options): void
  export const version: string
  export default class MyLibrary {
    constructor(options?: Options)
    start(): void
    stop(): void
  }
}

下一步

掌握 TypeScript 基础后,你可以继续学习:


东巴文(db-w.cn)—— 让 TypeScript 更简单