Skip to content

TypeScript 与 JavaScript 主要特性区别详解

目录

  1. 类型系统
  2. 类(Class)增强特性
  3. 接口(Interface)
  4. 泛型(Generics)
  5. 高级类型
  6. 装饰器(Decorators)
  7. 模块系统
  8. 其他重要特性
  9. 版本演进对比

类型系统

静态类型 vs 动态类型

JavaScript(动态类型)

javascript
let name = '张三'
name = 123 // ✅ 运行时可以改变类型,不报错
name = true // ✅ 任意类型都可以赋值

function add(a, b) {
  return a + b
}
add(1, 2) // 3
add('1', '2') // "12" - 字符串拼接,可能不是预期结果
add(1, '2') // "12" - 混合类型运算

TypeScript(静态类型)

typescript
let name: string = '张三'
name = 123 // ❌ 编译错误:不能将类型"number"分配给类型"string"

function add(a: number, b: number): number {
  return a + b
}
add(1, 2) // ✅ 正确
add('1', '2') // ❌ 编译错误:参数类型不匹配

基础类型对比

类型JavaScriptTypeScript
布尔值true/falseboolean
数字1, 1.5, 0xffnumber
字符串"hello"string
数组[1, 2, 3]number[]Array<number>
元组[string, number]
枚举enum Color {Red, Green}
任意类型所有值any
空值null/undefinedvoid, null, undefined
永不返回never
未知类型unknown

TypeScript 特有类型

typescript
// 元组 - 固定长度和类型的数组
let tuple: [string, number, boolean]
tuple = ['hello', 42, true] // ✅
tuple = ['hello', 42] // ❌ 长度不匹配
tuple = [42, 'hello', true] // ❌ 类型顺序不匹配

// 枚举
enum Direction {
  Up = 1,
  Down, // 2(自动递增)
  Left, // 3
  Right // 4
}

enum Color {
  Red, // 0
  Green, // 1
  Blue // 2
}

// 字符串枚举
enum UserRole {
  Admin = 'ADMIN',
  User = 'USER',
  Guest = 'GUEST'
}

// unknown - 比 any 更安全的任意类型
let notSure: unknown = 4
notSure = 'maybe a string'
notSure = false

if (typeof notSure === 'string') {
  console.log(notSure.toUpperCase()) // ✅ 类型收窄后可用
}

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

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

// 联合类型
let id: string | number
id = 123 // ✅
id = 'abc' // ✅
id = true // ❌

// 交叉类型
type Person = { name: string }
type Employee = { employeeId: number }
type Worker = Person & Employee

const worker: Worker = {
  name: '张三',
  employeeId: 1001
}

// 字面量类型
let status: 'pending' | 'approved' | 'rejected'
status = 'pending' // ✅
status = 'approved' // ✅
status = 'cancelled' // ❌

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

type ID = string | number
type Name = string
type Age = number

类(Class)增强特性

1. 类的属性声明与类型注解

JavaScript

javascript
class Person {
  constructor(name, age) {
    this.name = name // 运行时动态添加属性
    this.age = age
  }
}

const p = new Person('张三', 25)
console.log(p.name) // "张三"
p.name = 123 // ✅ 可以赋任意类型

TypeScript

typescript
class Person {
  // 必须先声明属性及其类型
  name: string
  age: number
  private id: string
  protected salary: number
  readonly birthYear: number

  constructor(name: string, age: number, id: string) {
    this.name = name
    this.age = age
    this.id = id
    this.birthYear = new Date().getFullYear() - age
  }
}

const p = new Person('张三', 25, 'P001')
p.name = '李四' // ✅
p.name = 123 // ❌ 类型错误
p.id = 'P002' // ❌ 私有属性,无法访问
p.birthYear = 1998 // ❌ 只读属性,无法修改

2. 访问修饰符

TypeScript 提供了三种访问修饰符,JavaScript 仅支持私有字段(ES2022+)。

typescript
class Employee {
  // public - 公有,任何地方可访问(默认)
  public name: string

  // private - 私有,只能在类内部访问
  private salary: number
  private _bonus: number = 0

  // protected - 受保护,类内部和子类可访问
  protected department: string

  // readonly - 只读,初始化后不可修改
  readonly employeeId: string

  // ES2022+ 私有字段语法(编译后保持私有)
  #secretCode: string = 'hidden'

  constructor(
    name: string,
    salary: number,
    department: string,
    employeeId: string
  ) {
    this.name = name
    this.salary = salary
    this.department = department
    this.employeeId = employeeId
  }

  // getter
  get bonus(): number {
    return this._bonus
  }

  // setter
  set bonus(value: number) {
    if (value >= 0) {
      this._bonus = value
    } else {
      console.error('奖金不能为负数')
    }
  }

  // 私有方法
  private calculateTax(): number {
    return this.salary * 0.1
  }

  // 受保护方法
  protected getDepartmentInfo(): string {
    return `部门: ${this.department}`
  }

  // 公有方法
  public getNetSalary(): number {
    return this.salary - this.calculateTax() + this._bonus
  }
}

class Manager extends Employee {
  private teamSize: number

  constructor(
    name: string,
    salary: number,
    department: string,
    employeeId: string,
    teamSize: number
  ) {
    super(name, salary, department, employeeId)
    this.teamSize = teamSize
  }

  // 可以访问 protected 成员
  public getManagerInfo(): string {
    return `${this.getDepartmentInfo()}, 团队人数: ${this.teamSize}`
  }

  // ❌ 无法访问 private 成员
  // getSalary() {
  //     return this.salary;  // 错误
  // }
}

const emp = new Employee('张三', 10000, '技术部', 'E001')
console.log(emp.name) // ✅ "张三"
// console.log(emp.salary);         // ❌ 私有属性
// console.log(emp.department);     // ❌ 受保护属性
emp.bonus = 5000 // ✅ 通过 setter 设置
console.log(emp.getNetSalary()) // ✅ 14500

3. 参数属性(简写语法)

TypeScript 允许在构造函数参数中直接声明并初始化属性:

typescript
// 传统写法
class Person1 {
  name: string
  age: number

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

// 参数属性简写
class Person2 {
  constructor(
    public name: string,
    public age: number,
    private id: string,
    protected email: string,
    readonly createdAt: Date = new Date()
  ) {
    // 自动创建并初始化属性,无需手动赋值
  }
}

const p = new Person2('张三', 25, 'P001', 'zhangsan@example.com')
console.log(p.name) // "张三"
console.log(p.createdAt) // 当前日期

4. 抽象类

TypeScript 支持抽象类,JavaScript 不支持:

typescript
// 抽象类 - 不能被实例化
abstract class Animal {
  // 抽象属性
  abstract name: string

  // 抽象方法 - 子类必须实现
  abstract makeSound(): void

  // 具体方法 - 子类继承
  public move(distance: number): void {
    console.log(`${this.name} 移动了 ${distance} 米`)
  }

  // 受保护的抽象方法
  protected abstract eat(): void
}

// 具体类必须实现所有抽象成员
class Dog extends Animal {
  name: string

  constructor(name: string) {
    super()
    this.name = name
  }

  makeSound(): void {
    console.log('汪汪汪!')
  }

  protected eat(): void {
    console.log(`${this.name} 正在吃狗粮`)
  }

  // 可以添加新方法
  public fetch(): void {
    console.log(`${this.name} 正在捡球`)
  }
}

class Cat extends Animal {
  name: string

  constructor(name: string) {
    super()
    this.name = name
  }

  makeSound(): void {
    console.log('喵喵喵!')
  }

  protected eat(): void {
    console.log(`${this.name} 正在吃鱼`)
  }
}

// const animal = new Animal();  // ❌ 无法创建抽象类实例
const dog = new Dog('旺财')
dog.makeSound() // "汪汪汪!"
dog.move(10) // "旺财 移动了 10 米"
dog.fetch() // "旺财 正在捡球"

const cat = new Cat('咪咪')
cat.makeSound() // "喵喵喵!"

5. 类实现接口

typescript
// 定义接口
interface IAnimal {
  name: string
  age: number
  makeSound(): void
}

interface IRunnable {
  run(speed: number): void
}

interface ISwimmable {
  swim(distance: number): void
}

// 实现单个接口
class Fish implements IAnimal {
  name: string
  age: number

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

  makeSound(): void {
    console.log('咕噜咕噜...')
  }
}

// 实现多个接口
class Duck implements IAnimal, IRunnable, ISwimmable {
  name: string
  age: number

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

  makeSound(): void {
    console.log('嘎嘎嘎!')
  }

  run(speed: number): void {
    console.log(`${this.name} 以 ${speed} km/h 的速度奔跑`)
  }

  swim(distance: number): void {
    console.log(`${this.name} 游了 ${distance} 米`)
  }
}

// 类可以同时继承和实现接口
abstract class BaseAnimal implements IAnimal {
  abstract name: string
  abstract age: number
  abstract makeSound(): void

  public sleep(hours: number): void {
    console.log(`${this.name} 睡了 ${hours} 小时`)
  }
}

class Lion extends BaseAnimal implements IRunnable {
  name: string
  age: number

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

  makeSound(): void {
    console.log('吼!!!')
  }

  run(speed: number): void {
    console.log(`狮子 ${this.name} 以 ${speed} km/h 奔跑`)
  }
}

6. 泛型类

typescript
// 泛型类
class Container<T> {
  private items: T[] = []

  add(item: T): void {
    this.items.push(item)
  }

  get(index: number): T | undefined {
    return this.items[index]
  }

  getAll(): T[] {
    return [...this.items]
  }

  remove(index: number): T | undefined {
    return this.items.splice(index, 1)[0]
  }

  get count(): number {
    return this.items.length
  }
}

// 使用泛型类
const numberContainer = new Container<number>()
numberContainer.add(1)
numberContainer.add(2)
numberContainer.add(3)
console.log(numberContainer.get(0)) // 1

const stringContainer = new Container<string>()
stringContainer.add('hello')
stringContainer.add('world')
console.log(stringContainer.getAll()) // ["hello", "world"]

// 多类型参数的泛型类
class Pair<K, V> {
  constructor(
    public key: K,
    public value: V
  ) {}

  public toString(): string {
    return `${String(this.key)}: ${String(this.value)}`
  }
}

const pair = new Pair<string, number>('age', 25)
console.log(pair.toString()) // "age: 25"

// 泛型约束
interface Lengthwise {
  length: number
}

class Collection<T extends Lengthwise> {
  private items: T[] = []

  add(item: T): void {
    this.items.push(item)
  }

  getTotalLength(): number {
    return this.items.reduce((sum, item) => sum + item.length, 0)
  }
}

const stringCollection = new Collection<string>()
stringCollection.add('hello') // string 有 length 属性
stringCollection.add('world')
console.log(stringCollection.getTotalLength()) // 10

// const numberCollection = new Collection<number>();  // ❌ number 没有 length 属性

7. 类的类型

typescript
class Point {
  x: number
  y: number

  constructor(x: number, y: number) {
    this.x = x
    this.y = y
  }

  add(point: Point): Point {
    return new Point(this.x + point.x, this.y + point.y)
  }
}

// 类作为类型使用
function printPoint(p: Point): void {
  console.log(`(${p.x}, ${p.y})`)
}

const p1 = new Point(1, 2)
const p2 = new Point(3, 4)
const p3 = p1.add(p2)

printPoint(p3) // "(4, 6)"

// 类表达式
const Person = class {
  constructor(public name: string) {}
  greet() {
    return `Hello, I'm ${this.name}`
  }
}

const person = new Person('张三')
console.log(person.greet()) // "Hello, I'm 张三"

// 构造签名
interface IPointConstructor {
  new (x: number, y: number): Point
}

function createPoint(ctor: IPointConstructor, x: number, y: number): Point {
  return new ctor(x, y)
}

const point = createPoint(Point, 5, 10)
console.log(point) // Point { x: 5, y: 10 }

8. this 类型

typescript
class Calculator {
  protected value: number = 0

  add(n: number): this {
    this.value += n
    return this // 返回 this 实现链式调用
  }

  subtract(n: number): this {
    this.value -= n
    return this
  }

  multiply(n: number): this {
    this.value *= n
    return this
  }

  divide(n: number): this {
    if (n !== 0) {
      this.value /= n
    }
    return this
  }

  getValue(): number {
    return this.value
  }
}

const calc = new Calculator()
const result = calc
  .add(10) // this.value = 10
  .multiply(2) // this.value = 20
  .subtract(5) // this.value = 15
  .divide(3) // this.value = 5
  .getValue()

console.log(result) // 5

// this 类型在继承中的应用
class ScientificCalculator extends Calculator {
  sin(): this {
    this.value = Math.sin(this.value)
    return this
  }

  cos(): this {
    this.value = Math.cos(this.value)
    return this
  }

  pow(exponent: number): this {
    this.value = Math.pow(this.value, exponent)
    return this
  }
}

const sciCalc = new ScientificCalculator()
sciCalc
  .add(1)
  .multiply(Math.PI / 2)
  .sin() // 返回类型仍然是 ScientificCalculator
  .getValue()

接口(Interface)

TypeScript 的接口是其核心特性之一,JavaScript 没有对应概念。

1. 基本接口

typescript
// 定义对象形状
interface Person {
  name: string
  age: number
  email?: string // 可选属性
  readonly id: string // 只读属性
}

const person: Person = {
  name: '张三',
  age: 25,
  id: 'P001'
  // email 是可选的,可以不提供
}

person.name = '李四' // ✅
// person.id = "P002";   // ❌ 只读属性不可修改

// 索引签名
interface StringArray {
  [index: number]: string
}

const arr: StringArray = ['a', 'b', 'c']
console.log(arr[0]) // "a"

interface StringDictionary {
  [key: string]: string | number
  name: string // 必须符合索引签名类型
  age: number // ✅ number 是 string | number 的子类型
}

const dict: StringDictionary = {
  name: '张三',
  age: 25,
  city: '北京'
}

2. 函数类型接口

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

const mySearch: SearchFunc = (src, sub) => {
  return src.indexOf(sub) !== -1
}

console.log(mySearch('hello world', 'world')) // true

// 构造签名
interface IAnimalConstructor {
  new (name: string): IAnimal
}

interface IAnimal {
  name: string
  makeSound(): void
}

class Dog implements IAnimal {
  constructor(public name: string) {}
  makeSound(): void {
    console.log('汪汪汪!')
  }
}

function createAnimal(ctor: IAnimalConstructor, name: string): IAnimal {
  return new ctor(name)
}

const dog = createAnimal(Dog, '旺财')
dog.makeSound() // "汪汪汪!"

3. 接口继承

typescript
// 单继承
interface Shape {
  color: string
}

interface Square extends Shape {
  sideLength: number
}

const square: Square = {
  color: 'red',
  sideLength: 10
}

// 多继承
interface Printable {
  print(): void
}

interface Loggable {
  log(): void
}

interface Document extends Printable, Loggable {
  content: string
}

class PDFDocument implements Document {
  constructor(public content: string) {}

  print(): void {
    console.log(`打印: ${this.content}`)
  }

  log(): void {
    console.log(`日志: ${this.content}`)
  }
}

// 接口继承类
class Control {
  private state: any

  protected getState(): any {
    return this.state
  }
}

// SelectableControl 继承了 Control 的私有成员
interface SelectableControl extends Control {
  select(): void
}

// 只有 Control 或其子类才能实现 SelectableControl
class Button extends Control implements SelectableControl {
  select(): void {
    console.log('按钮被选中')
  }
}

class TextBox extends Control implements SelectableControl {
  select(): void {
    console.log('文本框被选中')
  }
}

4. 接口与类型别名的区别

typescript
// 接口
interface Person {
  name: string
  age: number
}

// 接口可以被类实现
class Student implements Person {
  name: string
  age: number
  grade: string

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

// 接口可以声明合并
interface Person {
  email: string
}

// 现在 Person 有三个属性
const p: Person = {
  name: '张三',
  age: 25,
  email: 'zhangsan@example.com'
}

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

// 类型别名可以表示原始类型、联合类型、元组等
type ID = string | number
type Name = string
type Coordinate = [number, number]

// 类型别名不能被类实现
// class PointClass implements Point {}  // ❌ 不能实现类型别名

// 类型别名不能声明合并
type Point = {
  z: number // ❌ 重复的标识符
}

泛型(Generics)

1. 泛型函数

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

// 使用方式
const str = identity<string>('hello') // 显式指定类型
const num = identity(123) // 类型推断

// 泛型数组
function getFirst<T>(arr: T[]): T | undefined {
  return arr[0]
}

console.log(getFirst([1, 2, 3])) // 1
console.log(getFirst(['a', 'b', 'c'])) // "a"

// 多类型参数
function pair<K, V>(key: K, value: V): [K, V] {
  return [key, value]
}

const p = pair<string, number>('age', 25)
console.log(p) // ["age", 25]

// 泛型约束
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({ length: 10 }) // 10
// logLength(123);       // ❌ number 没有 length 属性

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

const obj = { name: '张三', age: 25 }
console.log(getProperty(obj, 'name')) // "张三"
// console.log(getProperty(obj, "email"));  // ❌ "email" 不是 obj 的属性

2. 泛型接口

typescript
// 泛型接口
interface Container<T> {
  value: T
  getValue(): T
  setValue(value: T): void
}

class Box<T> implements Container<T> {
  value: T

  constructor(initialValue: T) {
    this.value = initialValue
  }

  getValue(): T {
    return this.value
  }

  setValue(value: T): void {
    this.value = value
  }
}

const stringBox = new Box<string>('hello')
console.log(stringBox.getValue()) // "hello"

const numberBox = new Box<number>(42)
console.log(numberBox.getValue()) // 42

// 函数类型的泛型接口
interface IGenericFunc<T> {
  (arg: T): T
}

const genericIdentity: IGenericFunc<number> = arg => arg
console.log(genericIdentity(123)) // 123

3. 泛型约束与默认值

typescript
// 泛型默认类型
interface ApiResponse<T = any> {
  code: number
  message: string
  data: T
}

const response1: ApiResponse = {
  code: 200,
  message: 'success',
  data: { name: '张三' }
}

const response2: ApiResponse<string> = {
  code: 200,
  message: 'success',
  data: '操作成功'
}

// 多个类型参数的默认值
interface Page<T = any, U = number> {
  items: T[]
  total: U
  page: U
  pageSize: U
}

const page: Page<{ id: number; name: string }> = {
  items: [{ id: 1, name: '张三' }],
  total: 100,
  page: 1,
  pageSize: 10
}

// 泛型约束中使用类类型
class BeeKeeper {
  hasMask: boolean = true
}

class ZooKeeper {
  nametag: string = '管理员'
}

class Animal {
  numLegs: number = 4
}

class Bee extends Animal {
  keeper: BeeKeeper = new BeeKeeper()
  numLegs: number = 6
}

class Lion extends Animal {
  keeper: ZooKeeper = new ZooKeeper()
}

function createInstance<A extends Animal>(c: new () => A): A {
  return new c()
}

const bee = createInstance(Bee)
console.log(bee.keeper.hasMask) // true

const lion = createInstance(Lion)
console.log(lion.keeper.nametag) // "管理员"

高级类型

1. 交叉类型与联合类型

typescript
// 联合类型
type StringOrNumber = string | number

function formatValue(value: StringOrNumber): string {
  if (typeof value === 'string') {
    return value.toUpperCase()
  } else {
    return value.toFixed(2)
  }
}

// 交叉类型
type Name = { name: string }
type Age = { age: number }
type Email = { email: string }

type Person = Name & Age & Email

const person: Person = {
  name: '张三',
  age: 25,
  email: 'zhangsan@example.com'
}

// 混合使用
type Mixed = { id: number } & (string | { name: string })
// 等同于 { id: number } & string 或 { id: number } & { name: string }

2. 类型守卫与类型收窄

typescript
// typeof 类型守卫
function printValue(value: string | number) {
  if (typeof value === 'string') {
    console.log(value.toUpperCase()) // value 是 string
  } else {
    console.log(value.toFixed(2)) // value 是 number
  }
}

// instanceof 类型守卫
class Dog {
  bark() {
    console.log('汪汪汪!')
  }
}

class Cat {
  meow() {
    console.log('喵喵喵!')
  }
}

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

// in 操作符类型守卫
interface Bird {
  fly(): void
  layEggs(): void
}

interface Fish {
  swim(): void
  layEggs(): void
}

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

// 自定义类型守卫
interface Car {
  car: string
  drive(): void
}

interface Boat {
  boat: string
  sail(): void
}

function isCar(vehicle: Car | Boat): vehicle is Car {
  return (vehicle as Car).car !== undefined
}

function operate(vehicle: Car | Boat) {
  if (isCar(vehicle)) {
    vehicle.drive() // vehicle 是 Car
  } else {
    vehicle.sail() // vehicle 是 Boat
  }
}

// 可辨识联合
interface Circle {
  kind: 'circle'
  radius: number
}

interface Rectangle {
  kind: 'rectangle'
  width: number
  height: number
}

interface Triangle {
  kind: 'triangle'
  base: number
  height: number
}

type Shape = Circle | Rectangle | Triangle

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'rectangle':
      return shape.width * shape.height
    case 'triangle':
      return (shape.base * shape.height) / 2
  }
}

// 穷尽检查
function assertNever(x: never): never {
  throw new Error('Unexpected object: ' + x)
}

function getAreaExhaustive(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2
    case 'rectangle':
      return shape.width * shape.height
    case 'triangle':
      return (shape.base * shape.height) / 2
    default:
      return assertNever(shape) // 如果新增类型会报错
  }
}

3. 映射类型

typescript
// Readonly 映射类型
type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}

// Partial 映射类型
type Partial<T> = {
  [P in keyof T]?: T[P]
}

// Required 映射类型
type Required<T> = {
  [P in keyof T]-?: T[P]
}

// Pick 映射类型
type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}

// Omit 映射类型
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>

// Record 映射类型
type Record<K extends keyof any, T> = {
  [P in K]: T
}

// 使用示例
interface User {
  id: number
  name: string
  email: string
  age?: number
}

// 所有属性只读
type ReadonlyUser = Readonly<User>
// { readonly id: number; readonly name: string; readonly email: string; readonly age?: number; }

// 所有属性可选
type PartialUser = Partial<User>
// { id?: number; name?: string; email?: string; age?: number; }

// 所有属性必选
type RequiredUser = Required<User>
// { id: number; name: string; email: string; age: number; }

// 选择部分属性
type UserNameAndEmail = Pick<User, 'name' | 'email'>
// { name: string; email: string; }

// 排除部分属性
type UserWithoutId = Omit<User, 'id'>
// { name: string; email: string; age?: number; }

// 创建对象类型
type UserRoles = Record<'admin' | 'user' | 'guest', string[]>
// { admin: string[]; user: string[]; guest: string[]; }

4. 条件类型

typescript
// 基本条件类型
type IsString<T> = T extends string ? true : false

type A = IsString<string> // true
type B = IsString<number> // false

// 内置条件类型
type NonNullable<T> = T extends null | undefined ? never : T

type C = NonNullable<string | null | undefined> // string

type Exclude<T, U> = T extends U ? never : T
type D = Exclude<'a' | 'b' | 'c', 'a'> // "b" | "c"

type Extract<T, U> = T extends U ? T : never
type E = Extract<'a' | 'b' | 'c', 'a' | 'f'> // "a"

type ReturnType<T> = T extends (...args: any) => infer R ? R : any
type F = ReturnType<() => string> // string

type Parameters<T> = T extends (...args: infer P) => any ? P : never
type G = Parameters<(a: string, b: number) => void> // [string, number]

// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never
type H = ToArray<string | number> // string[] | number[]

// 非分布式条件类型
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type I = ToArrayNonDist<string | number> // (string | number)[]

5. 模板字面量类型

typescript
// 基本模板字面量类型
type World = 'world'
type Greeting = `hello ${World}` // "hello world"

// 联合类型展开
type EmailLocaleIDs = 'welcome_email' | 'email_heading'
type FooterLocaleIDs = 'footer_title' | 'footer_sendoff'

type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`
// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"

// 字符串操作类型
type UppercaseGreeting = Uppercase<Greeting> // "HELLO WORLD"
type LowercaseGreeting = Lowercase<Greeting> // "hello world"
type CapitalizeGreeting = Capitalize<Greeting> // "Hello world"
type UncapitalizeGreeting = Uncapitalize<Greeting> // "hello world"

// 类型推断
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}

interface Person {
  name: string
  age: number
}

type PersonGetters = Getters<Person>
// {
//     getName: () => string;
//     getAge: () => number;
// }

// 移除前缀
type RemovePrefix<T, P extends string> = T extends `${P}${infer Rest}`
  ? Rest
  : T
type J = RemovePrefix<'getUserName', 'get'> // "UserName"

装饰器(Decorators)

TypeScript 支持装饰器,JavaScript 装饰器提案仍在进行中。

1. 类装饰器

typescript
// 类装饰器
function sealed(constructor: Function) {
  Object.seal(constructor)
  Object.seal(constructor.prototype)
}

@sealed
class BankAccount {
  balance: number = 0

  deposit(amount: number) {
    this.balance += amount
  }
}

// 类装饰器工厂
function logger(prefix: string) {
  return function <T extends { new (...args: any[]): {} }>(constructor: T) {
    return class extends constructor {
      constructor(...args: any[]) {
        super(...args)
        console.log(`${prefix}: 创建实例,参数: ${JSON.stringify(args)}`)
      }
    }
  }
}

@logger('BankAccount')
class BankAccount2 {
  constructor(public balance: number) {}
}

const account = new BankAccount2(1000)
// 输出: BankAccount: 创建实例,参数: [1000]

2. 方法装饰器

typescript
// 方法装饰器
function enumerable(value: boolean) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    descriptor.enumerable = value
  }
}

class Person {
  constructor(private name: string) {}

  @enumerable(false)
  getSecret() {
    return `秘密: ${this.name}`
  }
}

// 方法装饰器 - 日志
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value

  descriptor.value = function (...args: any[]) {
    console.log(`调用 ${propertyKey},参数: ${JSON.stringify(args)}`)
    const result = originalMethod.apply(this, args)
    console.log(`结果: ${JSON.stringify(result)}`)
    return result
  }

  return descriptor
}

class Calculator {
  @log
  add(a: number, b: number): number {
    return a + b
  }
}

const calc = new Calculator()
calc.add(2, 3)
// 输出:
// 调用 add,参数: [2,3]
// 结果: 5

3. 属性装饰器

typescript
// 属性装饰器
function defaultValue(value: any) {
  return function (target: any, propertyKey: string) {
    Object.defineProperty(target, propertyKey, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    })
  }
}

class Config {
  @defaultValue('localhost')
  host!: string

  @defaultValue(8080)
  port!: number
}

const config = new Config()
console.log(config.host) // "localhost"
console.log(config.port) // 8080

4. 参数装饰器

typescript
// 参数装饰器
function required(target: any, propertyKey: string, parameterIndex: number) {
  const existingRequiredParameters: number[] =
    Reflect.getOwnMetadata('required', target, propertyKey) || []
  existingRequiredParameters.push(parameterIndex)
  Reflect.defineMetadata(
    'required',
    existingRequiredParameters,
    target,
    propertyKey
  )
}

function validate(
  target: any,
  propertyName: string,
  descriptor: TypedPropertyDescriptor<Function>
) {
  const method = descriptor.value!
  descriptor.value = function () {
    const requiredParameters: number[] =
      Reflect.getOwnMetadata('required', target, propertyName) || []

    for (const parameterIndex of requiredParameters) {
      if (
        parameterIndex >= arguments.length ||
        arguments[parameterIndex] === undefined
      ) {
        throw new Error(`参数 ${parameterIndex + 1} 是必需的`)
      }
    }

    return method.apply(this, arguments)
  }
}

class UserService {
  @validate
  createUser(@required name: string, @required email: string, age?: number) {
    console.log(`创建用户: ${name}, ${email}, ${age}`)
  }
}

const service = new UserService()
service.createUser('张三', 'zhangsan@example.com', 25) // ✅
// service.createUser("张三", undefined);  // ❌ 抛出错误

5. 装饰器元数据

typescript
import 'reflect-metadata'

const formatMetadataKey = Symbol('format')

function format(formatString: string) {
  return Reflect.metadata(formatMetadataKey, formatString)
}

function getFormat(target: any, propertyKey: string) {
  return Reflect.getMetadata(formatMetadataKey, target, propertyKey)
}

class Person {
  @format('Hello, %s')
  name: string

  constructor(name: string) {
    this.name = name
  }
}

const person = new Person('张三')
const formatString = getFormat(person, 'name')
console.log(formatString) // "Hello, %s"

模块系统

1. 模块导出

typescript
// module.ts

// 命名导出
export const PI = 3.14159

export interface Point {
  x: number
  y: number
}

export class Circle {
  constructor(public radius: number) {}

  getArea(): number {
    return PI * this.radius ** 2
  }
}

export function calculateDistance(p1: Point, p2: Point): number {
  return Math.sqrt((p2.x - p1.x) ** 2 + (p2.y - p1.y) ** 2)
}

// 默认导出
export default class Rectangle {
  constructor(
    public width: number,
    public height: number
  ) {}

  getArea(): number {
    return this.width * this.height
  }
}

// 重命名导出
export { Circle as CircleClass }
export { calculateDistance as distance }

// 重新导出
export * from './shapes'
export { Triangle } from './shapes'

2. 模块导入

typescript
// main.ts

// 导入默认导出
import Rectangle from './module'

// 导入命名导出
import { PI, Point, Circle, calculateDistance } from './module'

// 重命名导入
import { Circle as MyCircle } from './module'

// 导入所有
import * as Shapes from './module'

// 仅导入副作用
import './module'

// 使用
const circle = new Circle(5)
console.log(circle.getArea())

const rect = new Rectangle(10, 20)
console.log(rect.getArea())

const p1: Point = { x: 0, y: 0 }
const p2: Point = { x: 3, y: 4 }
console.log(calculateDistance(p1, p2)) // 5

3. 命名空间

typescript
// 命名空间(用于组织代码)
namespace Geometry {
  export interface Point {
    x: number
    y: number
  }

  export class Circle {
    constructor(
      public center: Point,
      public radius: number
    ) {}

    getArea(): number {
      return Math.PI * this.radius ** 2
    }
  }

  export class Rectangle {
    constructor(
      public topLeft: Point,
      public width: number,
      public height: number
    ) {}

    getArea(): number {
      return this.width * this.height
    }
  }

  // 非导出成员在命名空间外部不可见
  function helper(): void {
    console.log('helper function')
  }
}

const center: Geometry.Point = { x: 0, y: 0 }
const circle = new Geometry.Circle(center, 5)
console.log(circle.getArea())

// 命名空间合并
namespace Geometry {
  export interface Point3D extends Point {
    z: number
  }

  export class Sphere {
    constructor(
      public center: Point3D,
      public radius: number
    ) {}

    getVolume(): number {
      return (4 / 3) * Math.PI * this.radius ** 3
    }
  }
}

const center3D: Geometry.Point3D = { x: 0, y: 0, z: 0 }
const sphere = new Geometry.Sphere(center3D, 5)
console.log(sphere.getVolume())

4. 模块解析

typescript
// tsconfig.json 配置
{
    "compilerOptions": {
        "module": "commonjs",        // 模块系统: commonjs, amd, es2015, esnext
        "moduleResolution": "node",  // 模块解析策略: node, classic
        "baseUrl": "./src",          // 模块解析基础路径
        "paths": {
            "@/*": ["*"],            // 路径别名
            "@components/*": ["components/*"],
            "@utils/*": ["utils/*"]
        }
    }
}

// 使用路径别名
import { Button } from "@components/Button";
import { formatDate } from "@utils/date";

其他重要特性

1. 类型推断

typescript
// 变量类型推断
let x = 10 // 推断为 number
let y = 'hello' // 推断为 string
let z = [1, 2, 3] // 推断为 number[]
let obj = { a: 1, b: 'text' } // 推断为 { a: number; b: string; }

// 函数返回值推断
function add(a: number, b: number) {
  return a + b // 推断返回类型为 number
}

// 上下文类型推断
window.onmousedown = function (mouseEvent) {
  // mouseEvent 推断为 MouseEvent
  console.log(mouseEvent.button)
}

// 最佳通用类型
let mixed = [1, 'hello', true] // 推断为 (number | string | boolean)[]

// 类型断言
let someValue: any = 'this is a string'
let strLength1: number = (someValue as string).length
let strLength2: number = (<string>someValue).length

// 非空断言
function processValue(value: string | null) {
  // 使用 ! 断言 value 不为 null
  console.log(value!.toUpperCase())
}

// 确定赋值断言
let value!: number
initialize()
console.log(value) // ✅ 使用 ! 断言已赋值

function initialize() {
  value = 10
}

2. 类型兼容性

typescript
// 结构化类型系统(鸭子类型)
interface Point {
  x: number
  y: number
}

interface Point3D {
  x: number
  y: number
  z: number
}

const point3D: Point3D = { x: 1, y: 2, z: 3 }
const point: Point = point3D // ✅ Point3D 有 Point 的所有属性

// 函数参数双向协变
let fn1 = (a: number, b: string) => {}
let fn2 = (a: number) => {}

fn2 = fn1 // ✅ 参数少的可以赋给参数多的
// fn1 = fn2;  // ❌ 参数多的不能赋给参数少的

// 返回值协变
let fn3 = (): string => 'hello'
let fn4 = (): 'hello' => 'hello'

fn3 = fn4 // ✅ 返回值是子类型

// 枚举兼容性
enum Color {
  Red,
  Green,
  Blue
}
enum Status {
  Active,
  Inactive
}

let color: Color = Color.Red
let status: Status = Status.Active
// color = status;  // ❌ 不同枚举类型不兼容

// 类兼容性
class Animal {
  name: string = ''
}

class Dog extends Animal {
  breed: string = ''
}

let animal: Animal = new Dog() // ✅ 子类可以赋给父类

class Person {
  name: string = ''
  private age: number = 0
}

class Employee {
  name: string = ''
  private age: number = 0
}

// let p: Person = new Employee();  // ❌ 私有成员来源不同,不兼容

3. 声明文件

typescript
// global.d.ts - 全局声明文件
declare var jQuery: (selector: string) => any

declare function $(selector: string): any

declare namespace MyLib {
  interface Options {
    debug: boolean
  }

  function init(options: Options): void
}

// module.d.ts - 模块声明文件
declare module 'my-library' {
  export interface Config {
    apiKey: string
    timeout?: number
  }

  export function initialize(config: Config): void

  export class Client {
    constructor(config: Config)
    getData(): Promise<any>
  }

  export default Client
}

// 使用
import Client, { initialize, Config } from 'my-library'

const config: Config = {
  apiKey: 'your-api-key',
  timeout: 5000
}

initialize(config)
const client = new Client(config)

4. 类型操作符

typescript
// keyof 操作符
interface Person {
  name: string
  age: number
  email: string
}

type PersonKeys = keyof Person // "name" | "age" | "email"

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key]
}

// typeof 操作符
const person = {
  name: '张三',
  age: 25
}

type PersonType = typeof person // { name: string; age: number; }

// 索引访问类型
type NameType = Person['name'] // string
type AgeType = Person['age'] // number

// const 断言
const config = {
  host: 'localhost',
  port: 8080
} as const
// 类型变为 { readonly host: "localhost"; readonly port: 8080; }

const arr = [1, 2, 3] as const
// 类型变为 readonly [1, 2, 3]

// satisfies 操作符(TypeScript 4.9+)
type Colors = 'red' | 'green' | 'blue'

const theme = {
  primary: 'red',
  secondary: 'green'
} satisfies Record<string, Colors>

// theme.primary 类型为 "red",而不是 Colors

5. const 类型参数

typescript
// TypeScript 5.0+
function createArray<T extends readonly string[]>(...items: T): T {
  return items
}

const arr1 = createArray('a', 'b', 'c')
// 类型为 ["a", "b", "c"]

const arr2 = createArray<string>('a', 'b', 'c')
// 类型为 string[]

// 使用 const 类型参数
function createArrayConst<const T extends readonly string[]>(...items: T): T {
  return items
}

const arr3 = createArrayConst('a', 'b', 'c')
// 类型为 readonly ["a", "b", "c"]

版本演进对比

ES6+ 特性在 TypeScript 中的支持

特性JavaScriptTypeScript
let/constES6✅ 完全支持
箭头函数ES6✅ 完全支持 + 类型注解
ES6✅ 增强支持(修饰符、抽象类等)
模块ES6✅ 完全支持 + 命名空间
解构赋值ES6✅ 完全支持 + 类型注解
展开运算符ES6✅ 完全支持
PromiseES6✅ 完全支持 + async/await
async/awaitES2017✅ 完全支持
可选链 ?.ES2020✅ 完全支持
空值合并 ??ES2020✅ 完全支持
私有字段 #ES2022✅ 支持 + private 修饰符

TypeScript 独有特性

特性说明
静态类型编译时类型检查
接口定义对象形状
泛型类型参数化
装饰器元编程支持
枚举命名常量集合
命名空间代码组织方式
类型别名类型重命名
联合/交叉类型类型组合
类型守卫运行时类型检查
条件类型类型级别条件判断
映射类型类型转换
模板字面量类型字符串类型操作

编译目标配置

json
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022", // 编译目标版本
    "module": "ESNext", // 模块系统
    "lib": ["ES2022", "DOM"], // 包含的库声明
    "strict": true, // 严格模式
    "noImplicitAny": true, // 禁止隐式 any
    "strictNullChecks": true, // 严格空值检查
    "strictFunctionTypes": true, // 严格函数类型检查
    "noImplicitThis": true, // 禁止隐式 this
    "alwaysStrict": true, // 总是使用严格模式
    "noUnusedLocals": true, // 检查未使用的变量
    "noUnusedParameters": true, // 检查未使用的参数
    "noImplicitReturns": true, // 检查每个分支都有返回值
    "noFallthroughCasesInSwitch": true // 检查 switch 穿透
  }
}

总结

TypeScript 是 JavaScript 的超集,在保持 JavaScript 灵活性的同时,增加了强大的类型系统和面向对象特性。主要区别包括:

  1. 类型系统:TypeScript 提供静态类型检查,在编译时发现潜在错误
  2. 类增强:支持访问修饰符、抽象类、参数属性、泛型类等
  3. 接口:定义对象形状,支持继承、合并、实现
  4. 泛型:类型参数化,提高代码复用性
  5. 高级类型:联合类型、交叉类型、条件类型、映射类型等
  6. 装饰器:元编程支持,增强类、方法、属性、参数
  7. 模块系统:增强的模块支持,包括命名空间和路径别名

这些特性使得 TypeScript 更适合大型项目开发,提供更好的代码提示、重构支持和错误检测。

基于 VitePress 的本地知识库