现在位置: 首页 > TypeScript 教程 > 正文

TypeScript 简介

TypeScript 是由微软开发并开源的编程语言,它是 JavaScript 的超集,在完全兼容 JavaScript 语法的基础上,增加了可选的静态类型系统和基于类的面向对象编程能力。

TypeScript 代码最终会被编译为纯 JavaScript,可以运行在任何支持 JavaScript 的环境中,包括浏览器、Node.js 和移动端。


为什么需要 TypeScript

JavaScript 是一门动态类型语言,变量的类型在运行时才能确定。

这种灵活性在小型项目中表现出色,但随着项目规模扩大,问题逐渐暴露:

问题场景JavaScript 的困境TypeScript 的解决方式
函数参数传错类型运行时才报错,难以提前发现编译阶段即报错,IDE 实时提示
访问不存在的属性返回 undefined,行为难以预测编译器直接报错,拒绝通过
大型项目重构改一处,不知道哪里会崩类型系统自动追踪所有引用
团队协作函数接受什么参数、返回什么全靠注释或文档类型签名即文档,IDE 自动补全
代码可读性看函数定义无法直接知道数据结构接口和类型别名让数据结构一目了然

TypeScript 的本质目标不是取代 JavaScript,而是让大规模 JavaScript 工程变得可管理、可维护。它是 JavaScript 的"安全网",而不是竞争对手。

看一个最直观的例子:

实例

// JavaScript:运行时才会报错
function greet(name) {
    return "Hello, " + name.toUpperCase();
}

greet(123); // 运行时报错:name.toUpperCase is not a function

实例

// TypeScript:编译时就能发现错误
function greet(name: string): string {
    return "Hello, " + name.toUpperCase();
}

greet(123); // 编译错误:Argument of type 'number' is not assignable to parameter of type 'string'
greet("runoob"); // 正确:Hello, RUNOOB

TypeScript 与 JavaScript 的关系

TypeScript 和 JavaScript 之间的关系,可以用一句话概括:TypeScript 是 JavaScript 的超集,即所有合法的 JavaScript 代码,同时也是合法的 TypeScript 代码。

集合关系示意

JavaScript(所有 JS 代码)
  ⊂ TypeScript(JS + 类型系统 + 新特性)
    ⊂ 编译产物(标准 JavaScript,可在任意环境运行)

两者的核心差异对比:

对比项JavaScriptTypeScript
类型系统动态类型,运行时确定静态类型,编译时检查(可选)
运行方式直接在浏览器 / Node.js 中运行需先编译为 JS 再运行
错误发现时机运行时编译时(提前发现)
IDE 支持基础补全强类型推断、精准补全、重构支持
学习曲线较平缓需额外学习类型系统
现有 JS 兼容性完全兼容,可渐进式迁移
文件扩展名.js.ts.tsx(含 JSX)

TypeScript 支持"渐进式采用":你不必一次性重写整个项目。可以先把 .js 改为 .ts,逐步为关键模块添加类型,现有代码照常运行。


核心特性

TypeScript 在 JavaScript 的基础上引入了一套完整的类型系统,以下是最常用的核心特性。

基础类型注解

在变量、函数参数、返回值后用冒号声明类型,这是 TypeScript 最基础的写法。

实例

// 基础类型:number、string、boolean、null、undefined、symbol、bigint
let age: number = 25;
let username: string = "runoob";
let isActive: boolean = true;

// 数组类型,两种等价写法
let scores: number[] = [90, 85, 92];
let tags: Array<string> = ["typescript", "javascript"];

// 元组:固定长度和类型的数组
let point: [number, number] = [10, 20];
let entry: [string, number] = ["RUNOOB", 100];

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

// void:函数无返回值
function log(msg: string): void {
    console.log(msg);
}

// 可选参数:参数名后加 ?
function greet(name: string, title?: string): string {
    return title ? `${title} ${name}` : name;
}

console.log(greet("RUNOOB"));          // 输出:RUNOOB
console.log(greet("runoob", "Mr."));  // 输出:Mr. runoob

接口(Interface)

接口用来描述对象的"形状"(Shape),定义对象应该具备哪些属性和方法。

实例

// 定义接口:描述用户对象的结构
interface User {
    id: number;          // 必填
    name: string;        // 必填
    email?: string;      // 可选,使用 ? 标记
    readonly role: string; // 只读,赋值后不能修改
}

// 函数参数使用接口类型
function printUser(user: User): void {
    console.log(`ID: ${user.id}, Name: ${user.name}`);
    if (user.email) {
        console.log(`Email: ${user.email}`);
    }
}

const admin: User = {
    id: 1,
    name: "RUNOOB",
    email: "runoob@example.com",
    role: "admin",
};

printUser(admin);
// 输出:ID: 1, Name: RUNOOB
// 输出:Email: runoob@example.com

// admin.role = "user"; // 错误:Cannot assign to 'role' because it is a read-only property.

// 接口继承
interface AdminUser extends User {
    permissions: string[];
}

类型别名(Type Alias)

类型别名用 type 关键字创建,可以为任何类型定义别名,包括联合类型、交叉类型等复杂结构。

实例

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

let userId: ID = "abc-123"; // 合法
userId = 456;               // 也合法

// 字面量类型:限制变量只能取特定的值
type Direction = "up" | "down" | "left" | "right";
type Status = "pending" | "active" | "inactive";

function move(dir: Direction): void {
    console.log(`Moving ${dir}`);
}

move("up");    // 正确
// move("diagonal"); // 错误:不在字面量范围内

// 交叉类型:合并多个类型
type WithTimestamp = {
    createdAt: Date;
    updatedAt: Date;
};

type UserRecord = User & WithTimestamp; // 同时具有 User 和 WithTimestamp 的所有属性

// 函数类型别名
type Transformer<T, U> = (input: T) => U;
const toNumber: Transformer<string, number> = (s) => parseInt(s, 10);

泛型(Generics)

泛型允许编写可复用的组件,同时保持类型安全。可以把泛型理解为"类型的变量"——使用时再传入具体类型。

实例

// 没有泛型的写法:只能处理 number 类型
function firstNumber(arr: number[]): number {
    return arr[0];
}

// 使用泛型:T 是类型参数,调用时确定具体类型
function first<T>(arr: T[]): T {
    return arr[0];
}

const n = first<number>([1, 2, 3]);    // n 的类型是 number
const s = first<string>(["a", "b"]);  // s 的类型是 string
const inferred = first([true, false]); // TypeScript 自动推断 T 为 boolean

// 泛型接口
interface ApiResponse<T> {
    data: T;
    status: number;
    message: string;
}

// 使用泛型接口
const userResponse: ApiResponse<User> = {
    data: { id: 1, name: "RUNOOB", role: "admin" },
    status: 200,
    message: "success",
};

// 泛型约束:限制 T 必须有 id 属性
function getById<T extends { id: number }>(items: T[], id: number): T | undefined {
    return items.find(item => item.id === id);
}

枚举(Enum)

枚举用于定义一组命名常量,使代码中的"魔法数字"或"魔法字符串"变得有意义。

实例

// 数字枚举(默认从 0 开始自增)
enum Direction {
    Up,     // 0
    Down,   // 1
    Left,   // 2
    Right,  // 3
}

console.log(Direction.Up);    // 输出:0
console.log(Direction[0]);    // 输出:"Up"(反向映射)

// 字符串枚举(更推荐,调试时值更可读)
enum Color {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE",
}

function paint(color: Color): void {
    console.log(`Painting in ${color}`);
}

paint(Color.Red); // 输出:Painting in RED
// paint("red");  // 错误:字符串 "red" 不是 Color 类型

// const 枚举:编译后会被内联,减少运行时开销
const enum HttpStatus {
    OK = 200,
    NotFound = 404,
    InternalError = 500,
}

const status: HttpStatus = HttpStatus.OK; // 编译后直接替换为数字 200

类型推断

TypeScript 不需要为每个变量都手动标注类型,编译器会根据赋值自动推断。

实例

// TypeScript 自动推断变量类型,无需显式标注
let count = 0;            // 推断为 number
let name = "RUNOOB";      // 推断为 string
let flag = true;          // 推断为 boolean

// 推断数组元素类型
let numbers = [1, 2, 3];  // 推断为 number[]

// 推断函数返回值类型
function double(n: number) {  // 返回值自动推断为 number
    return n * 2;
}

// 对象字面量推断
const config = {
    host: "localhost",    // string
    port: 3000,           // number
    debug: false,         // boolean
};

// config.port = "3000"; // 错误:Type 'string' is not assignable to type 'number'.

一个实用建议:函数的参数通常需要显式标注类型(因为调用方无法从参数值推断类型),而函数体内的局部变量和返回值大多数情况下依赖推断即可,不必全部手动标注。

类与访问修饰符

TypeScript 完整支持 ES6 的类语法,并新增了 publicprivateprotectedreadonly 四种访问修饰符。

实例

class Animal {
    readonly name: string;         // 只读,初始化后不能修改
    private age: number;           // 私有,只能在类内部访问
    protected species: string;     // 受保护,子类可访问

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

    // public 方法(默认即为 public,可省略)
    public introduce(): string {
        return `I'm ${this.name}, a ${this.species}.`;
    }

    // getter:像访问属性一样调用方法
    get info(): string {
        return `${this.name} (${this.age} years old)`;
    }
}

class Dog extends Animal {
    private breed: string;

    constructor(name: string, age: number, breed: string) {
        super(name, age, "Canis lupus familiaris"); // 调用父类构造函数
        this.breed = breed;
    }

    // 子类可以访问 protected 属性
    describe(): string {
        return `${this.name} is a ${this.species}, breed: ${this.breed}`;
    }
}

const dog = new Dog("RUNOOB", 3, "Labrador");
console.log(dog.introduce());  // 输出:I'
m RUNOOB, a Canis lupus familiaris.
console.log(dog.info);         // 输出:RUNOOB (3 years old)
// console.log(dog.age);       // 错误:私有属性无法从外部访问

装饰器(Decorator)

装饰器是一种元编程语法,用于在类、方法、属性上附加额外行为,常见于 Angular、NestJS 等框架。

TypeScript 5.0 已将装饰器实现为正式标准(Stage 3 提案),不再需要开启 experimentalDecorators 标志。

实例

// 类装饰器:在类定义时执行
function sealed(constructor: Function) {
    Object.seal(constructor);         // 禁止添加新属性
    Object.seal(constructor.prototype);
}

// 方法装饰器:可用于日志记录、权限校验等
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const original = descriptor.value;
    descriptor.value = function (...args: any[]) {
        console.log(`Calling ${propertyKey} with args:`, args);
        const result = original.apply(this, args);
        console.log(`${propertyKey} returned:`, result);
        return result;
    };
}

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

const calc = new Calculator();
calc.add(1, 2);
// 输出:Calling add with args: [ 1, 2 ]
// 输出:add returned: 3

应用领域

TypeScript 凭借其对 JavaScript 的完全兼容性,几乎可以用在所有 JavaScript 能运行的地方,并且在大型项目中带来额外收益。

前端 Web 开发

这是 TypeScript 使用最广泛的场景。三大主流前端框架均对 TypeScript 提供一级支持:

框架TypeScript 支持情况典型场景
Angular官方语言,默认使用 TypeScript,无法绕过企业级 SPA、后台管理系统
React官方提供 @types/react,Create React App 和 Vite 均内置 TypeScript 模板电商、内容平台、中后台应用
VueVue 3 核心代码用 TypeScript 重写,组合式 API 对 TypeScript 友好度显著提升中小型项目、渐进式迁移
Next.js / Nuxt内置 TypeScript 支持,新项目默认开启全栈 SSR/SSG 应用

后端 Node.js 开发

TypeScript 在 Node.js 后端开发中同样普及。NestJS 是目前最流行的 TypeScript 后端框架,采用与 Angular 相近的模块化架构,内置依赖注入和装饰器支持。

此外,Deno 运行时从第一天起就原生支持 TypeScript,无需任何配置即可直接运行 .ts 文件。Bun 同样内置 TypeScript 支持。

命令行工具与脚本

借助 ts-nodetsx 等工具,TypeScript 代码可以在不预编译的情况下直接执行,非常适合编写构建脚本和 CLI 工具。

移动端与跨平台

React Native 完整支持 TypeScript,大量企业级移动应用(如 Microsoft Office 移动版、Shopify 等)均采用此技术栈。Expo 的新项目模板默认也以 TypeScript 为基础。

游戏开发与图形

Babylon.js(微软出品的 3D 引擎)完全用 TypeScript 编写,并将 TypeScript 类型定义作为一等公民。Phaser 等 2D 游戏引擎同样提供完整的类型定义。


发展历史

TypeScript 的诞生和成长历程与 JavaScript 生态的演变密切相关,每一次重大版本发布背后都有清晰的工程需求驱动。

起源(2010 - 2012)

TypeScript 的故事始于微软内部的一个工程挑战:如何让大型 JavaScript 项目变得可维护。

设计者 Anders Hejlsberg 是业界传奇——C# 的首席架构师,也是 Turbo Pascal 和 Delphi 的创造者。他在 2010 年左右开始设计这门语言,核心目标是在不破坏 JavaScript 兼容性的前提下,引入静态类型检查。

微软推出 TypeScript 的另一个背景是 Windows 8 的发布——应用程序可以使用 HTML + JavaScript 开发,微软希望吸引 .NET 程序员参与,因此 TypeScript 的许多语法有意与 C# 和 .NET 保持相近。

2012 年 10 月,TypeScript 0.8 公开发布,此前已在微软内部开发了约两年。

早期版本(2013 - 2015)

时间版本重要事件
2013-060.9正式稳定版发布,性能大幅提升,IDE 整合更完善
2014-041.0第一个正式稳定版,引入类、接口、模块,支持编译为标准 JavaScript
2014-07TypeScript 编译器代码开源,托管于 GitHub,接受社区贡献
2015-04微软发布 Visual Studio Code,内置对 TypeScript 的深度支持,两者相互成就
2015-071.5引入 ES6 模块语法支持、装饰器(实验性)、命名空间

高速成长(2016 - 2019)

这一阶段是 TypeScript 从小众工具走向主流的关键时期,核心推动力来自框架生态的选择。

时间版本 / 事件里程碑意义
2016-09TypeScript 2.0引入非空类型(--strictNullChecks),这是 TypeScript 类型安全最重要的特性之一;引入标记联合类型
2016Angular 2 发布Google Angular 团队宣布采用 TypeScript 作为官方开发语言,将 TypeScript 带入大众视野
2017TypeScript 2.x 系列条件类型、映射类型、infer 关键字相继推出,类型系统表达能力大幅提升
2018-07TypeScript 3.0项目引用(Project References),支持大型 monorepo;引入 unknown 类型(比 any 更安全的顶级类型)
2019DefinitelyTyped社区维护的类型定义仓库 DefinitelyTyped(@types/*)超过 7000 个包,基本覆盖所有主流 npm 库

成熟与普及(2020 - 2022)

时间版本 / 事件重要内容
2020-08TypeScript 4.0可变元组类型、标记元组元素;编辑器体验大幅改善
2021TypeScript 4.x 系列模板字符串类型(Template Literal Types)、内置工具类型增强、noImplicitOverride 等严格模式增强
2022State of JS 调查TypeScript 使用率首次超越纯 JavaScript,成为 JS 生态中最受欢迎的超集语言
2022Vue 3 / Vite 普及Vue 3 核心用 TypeScript 重写,配合 Vite 让前端工程的 TypeScript 体验达到新高度

现代阶段(2023 至今)

时间版本重要特性
2023-03TypeScript 5.0现代化装饰器(Stage 3 标准)、const 类型参数、所有枚举升级为联合枚举,性能显著提升
2023-08TypeScript 5.2引入 Explicit Resource Management(using 关键字),解决数据库连接、文件句柄等资源的确定性清理问题
2024-03TypeScript 5.4闭包中类型收窄改进,NoInfer 工具类型
2024-06TypeScript 5.5类型谓词推断,正则表达式语法检查,让类型系统对代码逻辑理解更精准
2024-11TypeScript 5.7支持 --target es2024,未初始化变量检测更可靠
2025-03TypeScript 5.8条件和索引访问类型的返回值检查增强;支持在 --module nodenext 下通过 require() 加载 ESM 模块(需 Node.js 22+)

未来展望:用 Go 重写编译器

2025 年初,微软宣布了一项重大决定:以 Go 语言重写 TypeScript 编译器,项目代号 Corsa

官方表示,重写后的编译器性能提升预计超过 10 倍,大型项目的构建时间将从分钟级降至秒级。

按照规划,Go 版本将在 TypeScript 7.x 正式发布,目前 5.x / 6.x 的 TypeScript 版本继续正常迭代和维护。预计 2025 年末完成重构工作。

这次重写并不会改变 TypeScript 语言本身的语法或类型系统,对开发者而言是透明的升级——你的代码不需要任何改动,只是编译速度大幅提升。


TypeScript 与其他强类型语言的对比

如果你有其他强类型语言的背景,以下对比可以帮助你快速定位 TypeScript 的设计理念。

对比项TypeScriptJava / C#Go
类型系统结构化类型(鸭子类型)名义类型(必须显式声明继承关系)结构化类型(隐式接口)
空值安全开启 strictNullChecks 后支持Java 需借助注解或 Optional,C# 8+ 支持通过 error 值和 nil 检查
泛型支持,语法与 Java/C# 相近支持,Java 有类型擦除限制Go 1.18+ 支持,语法较简洁
编译产物JavaScript(在 JS 环境运行)字节码(在 JVM / CLR 上运行)原生机器码
运行时类型检查无(类型信息编译后抹除)有(反射机制)
学习曲线对 JS 开发者友好,可渐进采用对初学者较陡相对简洁,学习曲线中等

TypeScript 使用的是结构化类型系统(Structural Typing):只要两个类型的"形状"一致,就认为它们是兼容的,不要求显式声明继承关系。这与 Java/C# 的名义类型(Nominal Typing)有本质区别,也是 TypeScript 能与 JavaScript 无缝互操作的关键原因。


TypeScript 的局限性

TypeScript 不是银弹,了解它的边界同样重要。

局限说明应对思路
运行时无类型类型信息在编译后全部抹除,运行时无法依赖类型做判断需要运行时校验时使用 zodio-ts 等库
编译步骤相比纯 JS 多了一个编译环节,增加了工程复杂度现代构建工具(Vite、esbuild)对此已有良好优化
any 类型逃生舱滥用 any 会让类型检查形同虚设开启 noImplicitAnystrict 模式,配合 ESLint 规则
类型体操门槛复杂的条件类型和映射类型对初学者不友好大多数业务代码不需要高级类型,循序渐进即可
第三方库支持少数老旧库缺少类型定义,需要自行编写 .d.ts先查 @types/*,确实没有再手写声明文件