TypeScript精要

61 阅读6分钟

TypeScript精要

JavaScript的超集,为大型应用开发设计的强类型语言。 2023年GitHub调查显示,TypeScript使用率已突破70%,成为前端开发主流选择。 TypeScript 的精要在于通过静态类型提升代码健壮性,结合模块化、泛型和高级类型实现高效开发。 其核心优势在于编译时类型检查、代码可维护性增强。

基础类型

  • 原始类型:number(数字)、string(字符串)、boolean(布尔值)、void(无返回值)、null 和 undefined(空值)。
  • 复合类型:any(任意类型,需谨慎使用)、never(永不存在的类型,如异常函数返回值)、object(非原始类型对象)。
  • 特殊类型:tuple(元组,固定长度和类型的数组)、enum(枚举,语义化常量集合)。
// 原始类型
let isDone: boolean = false;
let count: number = 5;
let name: string = "TypeScript";

// 数组类型(两种写法)
let list: number[] = [1, 2, 3];
let genericList: Array<number> = [1, 2, 3];

// 元组类型(固定长度和类型)
let tuple: [string, number] = ["hello", 10]; 

// 特殊类型
let nullable: null = null;      // 只能赋值为null
let undef: undefined = undefined; 
let nothing: void = undefined;  // 常用于函数无返回值
let neverVar: never = (() => { throw Error() })(); // 永远不存在的值

// 枚举类型(数字/字符串)
enum Color { Red = 1, Green = 2, Blue = 4 }
enum Direction { Up = "UP", Down = "DOWN" }

// 对象类型
let obj: object = { key: "value" };

// 任意类型(慎用)
let notSure: any = 4;
notSure = "maybe a string"; 

// 字面量类型
let sex: "male" | "female"; // 只允许这两个值
let magicNumber: 0 | 1 | 2; // 数字字面量类型

变量

在 TypeScript 中,变量声明的关键字主要有 var、let 和 const,它们在作用域、可变性、变量提升等方面有显著区别。

var声明

  • 作用域:函数级别的,即从声明点开始到函数结束。
  • 可变性:可以被重新赋值。
  • 变量提升:会被提升到函数作用域的顶部,即可以在声明之前访问。
  • 类型推断:若未显式声明类型,var 可能被推断为 any 类型。
function test() {
  var a = 10;
  if (true) {
    var a = 20; // 同一作用域内重复声明,仅最后一个生效
    console.log(a); // 输出 20
  }
  console.log(a); // 输出 20(函数内全局可见)
}

console.log(b); // undefined
var b = 1;      // 变量提升:声明被提升,但值初始化在声明时

let声明

  • 作用域:块级作用域,变量仅在 {} 内有效,如函数、if、for 等代码块。
  • 可变性:同一作用域内不可重复声明同一变量,否则报错。允许修改变量值(与 const 不同)。
  • 变量提升:变量在声明前不可访问,否则报错暂时性死区(与 var 的变量提升不同)。
if (true) {
  let b = 30;
  console.log(b); // 输出 30
}
console.log(b); // ReferenceError(块外不可访问)

{ 
  console.log(c); // ReferenceError,暂时性死区
  let c = 40;
}

const声明

  • 作用域:块级作用域,声明时必须赋值,否则报错。
  • 可变性:不可修改,一旦赋值后不可再次赋值,但复合类型(对象、数组)的内部属性可修改。
  • 变量提升:变量在声明前不可访问。
const arr: number[] = ;
arr.push(3); // 允许修改数组内容
arr = ;   // 报错:不能重新赋值

最佳实践

  • 优先使用 const(除非需要修改值),其次使用 let,避免使用 var(因其作用域和变量提升易引发问题)。
  • 结合 TypeScript 的类型系统,显式声明变量类型以提升代码安全性。

类与面向对象

类(Class)是面向对象编程的核心概念,其定义涉及多个关键知识点。以下是主要知识点的综合总结,结合了类的结构、修饰符、继承、抽象类等核心特性。

属性与方法

类通过 class 关键字定义,包含属性、构造函数和方法。

class Person {
  name: string; // 属性
  constructor(name: string) { this.name = name; } // 构造函数
  greet() { console.log(`Hello,  $ {this.name}!`); } // 方法
}

构造函数

构造函数用于初始化实例,若未显式定义,TypeScript 会生成默认空构造函数。

class Person {
  name: string;
  constructor(name: string) { this.name = name; } // 构造函数
}

访问控制修饰符

  • public:默认,类内外均可访问,公共 API 或可修改属性。
  • private:仅类内部访问,隐藏内部实现细节。
  • protected:类和子类可访问,共享给子类的私有逻辑。
class BankAccount {
  private balance: number; // 仅类内访问
  protected accountNumber: string; // 类和子类可访问
  public deposit(amount: number) { /* ... */ } // 公共方法
}

静态方法

静态成员属于类本身,而非实例,通过类名直接访问。

class Database {
  static instance: Database | null = null; // 静态属性
  static getInstance() { // 静态方法
    if (!this.instance) this.instance = new Database();
    return this.instance;
  }
}

继承与多态

使用 extends 关键字实现单继承,子类可继承父类的非私有成员。

  • 构造函数调用:子类构造函数必须通过 super() 调用父类构造函数。
  • 方法重写:子类可覆盖父类方法。
class Animal {
  constructor public name: string) { }
  abstract say(): void;
}

class Dog extends Animal {
  say() { console.log ` $ {this.name} 汪汪`; }
}

抽象类

用 abstract 关键字声明,不能被实例化,仅作为基类。 强制子类实现抽象方法,定义通用接口规范。

abstract class Shape {
  abstract area(): number;
}

class Circle extends Shape {
  radius: number;
  area() { return Math.PI * this radius ** 2; }
}

接口实现

使用 implements 关键字让类实现接口,确保类符合接口的结构要求。

interface IRenderable {
  render(): void;
}

class Component implements IRenderable {
  render() { /* ... */ }
}

interface与type的区别

interface
  • 面向对象设计:主要用于描述对象的结构(如属性、方法),强调类型契约和可扩展性。
  • 典型场景:定义类、函数、事件的形状,或通过 implements 实现类的接口约束。
interface Person {
  name: string;
  greet(): void;
}
class User implements Person { /* ... */ }

此外,interface支持同名接口的自动合并,适用于模块化扩展类型定义。type禁止同名重复声明,否则报错。

interface User { name: string; }
interface User { age: number; } // 合并为 { name: string; age: number; }
type
  • 类型别名:为已有类型(基本类型、联合类型、元组等)创建别名,支持更灵活的类型操作。
  • 典型场景:定义联合类型、交叉类型、工具类型(如 Partial),或简化复杂类型表达式。
type StringOrNumber = string | number;
type Admin = { role: "admin" };
继承与扩展
  • interface 使用 extends 继承其他接口或类型别名,支持逐步扩展。
  • type 使用 & 实现交叉类型组合,但无法直接继承。
// interface
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }

// type
type Animal = { name: string; };
type Dog = Animal & { breed: string; };

泛型

泛型是一种在定义函数、类或接口时不指定具体类型,而是在使用时再指定类型的技术。

泛型函数

泛型函数通过类型参数(如 T)定义,允许函数在调用时动态指定具体类型,避免使用 any 或联合类型导致的类型信息丢失。

function identity<T>(arg: T): T {
  return arg;
}
// 调用方式:
// 显式指定类型:identity<string>("hello")
// 类型推断:identity("hello")(编译器自动推断 T 为 string)

泛型类

泛型类允许类的属性和方法使用类型参数,适用于需要类型安全的集合或容器类。

class Stack<T> {
  private items: T[] = [];
  push(item: T) { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
}

泛型接口

泛型接口通过类型参数定义可复用的类型结构,常用于约束函数或类的行为。

interface IArr<T> {
  (value: T, count: number): T[];
}
const createArr: IArr<string> = (value, count) => new Array(count).fill(value);

createArr("hello", 3);   // ✅ 正确(返回 ["hello", "hello", "hello"])
createArr(123, 3);       // ❌ 错误(类型不匹配)

泛型约束

通过 extends 关键字限制泛型类型必须满足特定条件(如包含某些属性或继承某个类)

  • 应用场景:
    • 确保泛型类型具有特定属性(如 string 或 Array)。
    • 结合联合类型或工具类型(如 Partial)增强灵活性。
interface HasLength {
  length: number;
}
function loggingIdentity<T extends HasLength>(arg: T): T {
  console.log(arg.length); // 安全访问 .length 属性
  return arg;
}