TypeScript 教程:从入门到精通
目录
- 什么是 TypeScript?
- 为什么要用 TypeScript?
- 环境搭建与第一个程序
- 核心基础:类型系统
- 1. 基础类型
- 2. 接口
- 3. 类型别名
- 4. 函数
- 5. 类
- 6. 泛型
- 进阶概念
- 1. 枚举
- 2. 类型推断
- 3. 联合类型与类型保护
- 4. 类型断言
- 5. 命名空间与模块
- 实践:与现有 JavaScript 项目集成
- 最佳实践与工具链
- 学习资源总结
什么是 TypeScript?
TypeScript 是由微软开发的开源编程语言,它是 JavaScript 的一个超集,意味着任何合法的 JavaScript 代码也是合法的 TypeScript 代码。

它的核心特性是静态类型检查,你可以在编写代码时为变量、函数参数、函数返回值等指定类型,这样,TypeScript 编译器可以在代码运行前就发现类型相关的错误,从而提高代码的健壮性和可维护性。
一个简单的比喻:
- JavaScript 就像说一门没有语法规则的语言,别人可能能听懂,但你很容易说错,造成误解。
- TypeScript 就像在这门语言上增加了严格的语法规则和词典,你在开口前就必须检查用词是否正确,从而大大降低了沟通出错的概率。
TypeScript 代码会被编译成纯 JavaScript 代码,以便在任何支持 JavaScript 的环境(如浏览器、Node.js)中运行。
为什么要用 TypeScript?
- 类型安全: 这是最核心的优势,在开发阶段就能捕获 70% 以上的常见错误,比如调用一个不存在的方法、给函数传错类型的参数等,这减少了运行时崩溃的风险。
- 更好的代码提示与自动补全: 当你使用 VS Code 等现代编辑器时,TypeScript 能提供极其智能的代码提示,你输入变量名后,编辑器就知道这个变量有哪些属性和方法,从而大大提高开发效率。
- 提高代码可读性与可维护性: 类型本身就是一种文档,通过阅读函数签名(
function greet(name: string): string),你就能立刻明白这个函数需要什么类型的参数,以及会返回什么类型的结果,这使得代码逻辑更清晰,更易于团队协作和后期维护。 - 重构的信心: 当项目变得庞大时,修改一个变量可能会影响到很多地方,有了 TypeScript,当你重命名一个变量或修改其类型时,编译器会告诉你所有需要同步修改的地方,让你在重构时更有信心。
- 渐进式采用: 你可以逐步地将 TypeScript 引入到现有的 JavaScript 项目中,可以先从新文件开始,或者用
any类型暂时绕过类型检查,不会“一上来就要求所有代码都改成 TypeScript”。
环境搭建与第一个程序
步骤 1: 安装 Node.js 和 npm
TypeScript 需要通过 npm 来安装,所以请先确保你已经安装了 Node.js(它会自动包含 npm)。

步骤 2: 安装 TypeScript
打开你的终端,全局安装 TypeScript 编译器(tsc):
npm install -g typescript
你可以通过 tsc -v 来检查是否安装成功。
步骤 3: 创建项目并编译
-
创建一个新的项目文件夹并进入:
mkdir my-ts-project cd my-ts-project
-
初始化
package.json文件(虽然非必需,但推荐):
(图片来源网络,侵删)npm init -y
-
创建你的第一个 TypeScript 文件,
index.ts:// index.ts function greet(name: string) { console.log(`Hello, ${name}!`); } let user = "TypeScript World"; greet(user); -
编译 TypeScript 文件: 在终端中运行:
tsc index.ts
你会发现同级目录下生成了一个
index.js文件,这就是编译后的 JavaScript 代码:// index.js function greet(name) { console.log(`Hello, ${name}!`); } let user = "TypeScript World"; greet(user); -
运行 JavaScript 文件:
node index.js
终端将输出:
Hello, TypeScript World!
VS Code 用户福利:
VS Code 内置了对 TypeScript 的优秀支持,你只需要安装 TypeScript 插件(通常已预装),在打开 .ts 文件时,它会自动在后台进行类型检查,并在编辑器中直接标出错误,无需手动运行 tsc。
核心基础:类型系统
1. 基础类型
// boolean
let isDone: boolean = false;
// number (支持十进制、十六进制、二进制、八进制)
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
// string
let color: string = "blue";
// 也可以使用模板字符串
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}. I'll be ${age + 1} years old next month.`;
// array
// 方式一:在元素类型后面接上 []
let list: number[] = [1, 2, 3];
// 方式二:使用数组泛型
let list2: Array<number> = [1, 2, 3];
// Tuple (元组): 已知元素数量和类型的数组
let x: [string, number];
x = ["hello", 10]; // OK
// x = [10, "hello"]; // Error: Type 'number' is not assignable to type 'string'.
// enum (枚举): 见下一节
// any: 任意类型,绕过类型检查
let notSure: any = 4;
notSure = "maybe a string";
notSure = false;
// void: 表示没有任何类型,通常用于函数返回值
function logMessage(message: string): void {
console.log(message);
}
// null 和 undefined
let u: undefined = undefined;
let n: null = null;
// never: 表示永不存在的值的类型,一个总会抛出异常的函数
function error(message: string): never {
throw new Error(message);
}
// object: 表示非原始类型
let obj: object = { name: "John" };
2. 接口
接口是 TypeScript 的核心概念之一,它是一种强大的方式来定义对象的形状。
// 定义一个接口
interface User {
name: string;
age: number;
// 可选属性,用 ? 标记
email?: string;
}
// 使用接口
function printUser(user: User) {
console.log(`Name: ${user.name}, Age: ${user.age}`);
if (user.email) {
console.log(`Email: ${user.email}`);
}
}
let myUser: User = {
name: "Alice",
age: 30,
email: "alice@example.com"
};
printUser(myUser); // OK
// printUser({ name: "Bob" }); // Error: Property 'age' is missing
3. 类型别名
类型别名用来给一个类型起个新名字,它与接口非常相似,但可以用于原始类型、联合类型、元组等更广泛的场景。
type ID = number | string; // 联合类型
type Point = {
x: number;
y: number;
};
// 使用类型别名
let point: Point = { x: 10, y: 20 };
// 接口 vs 类型别名
// 1. 可以声明合并:多个同名接口会合并成一个
// 2. 类型别名不能被声明合并
// 3. 类型别名可以声明基本类型、联合类型、元组等,而接口不行
4. 函数
// 为函数参数和返回值添加类型注解
function add(x: number, y: number): number {
return x + y;
}
// 箭头函数
const multiply = (x: number, y: number): number => {
return x * y;
};
// 函数类型
let myAdd: (x: number, y: number) => number;
myAdd = add;
// 可选参数和默认参数
// 可选参数必须跟在必需参数后面
function buildName(firstName: string, lastName?: string): string {
if (lastName) {
return `${firstName} ${lastName}`;
}
return firstName;
}
// 默认参数
function buildNameWithDefault(firstName: string, lastName: string = "Smith"): string {
return `${firstName} ${lastName}`;
}
5. 类
TypeScript 提供了对 ES2025 类语法的全面支持,并增加了类型注解。
interface AnimalInterface {
name: string;
makeSound(): void;
}
class Animal implements AnimalInterface {
// public 是默认的
public name: string;
// private 只能在类内部访问
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// protected 只能在类内部及其子类中访问
protected makeSoundInternal(): void {
console.log("Some generic sound");
}
public makeSound(): void {
this.makeSoundInternal();
}
}
class Dog extends Animal {
// 子类构造函数必须调用 super()
constructor(name: string, age: number) {
super(name, age);
}
// 重写父类方法
public makeSound(): void {
console.log("Woof! Woof!");
}
}
const myDog = new Dog("Rex", 5);
myDog.makeSound(); // 输出: Woof! Woof!
console.log(myDog.name); // 输出: Rex
// console.log(myDog.age); // Error: 'age' is private
6. 泛型
泛型是一种创建可重用组件的工具,它允许你在定义函数、接口或类时不预先指定具体的类型,而是在使用时再指定。
// 1. 泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 调用方式一:明确指定类型
let output1 = identity<string>("myString");
// 调用方式二:类型推断
let output2 = identity(42); // 类型推断为 number
// 2. 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
let myIdentity: GenericIdentityFn<number> = identity;
// 3. 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
const myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
进阶概念
1. 枚举
枚举是对 JavaScript 标准功能的一个补充,它允许你定义一组命名常量。
// 默认枚举 (数字枚举)
enum Color {
Red, // 0
Green, // 1
Blue // 2
}
let c: Color = Color.Green;
// 可以手动赋值
enum Status {
Success = 200,
NotFound = 404,
Error = 500
}
// 字符串枚举
enum Message {
Success = "SUCCESS",
Failure = "FAILURE",
}
// 常量枚举
// 会被编译成内联的值,不会生成一个对象
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [Directions.Up, Directions.Down]; // 编译后: let directions = [0, 1];
2. 类型推断
TypeScript 会在没有明确指定类型的情况下,尝试自动推断出变量的类型。
// 推断为 number
let x = 3;
// 推断为 string
let y = "hello";
// 推断为 { a: number, b: number }
let z = { a: 1, b: 2 };
// 在函数中,参数需要显式注解,但返回值可以推断
function add(a: number, b: number) {
return a + b;
}
// 推断 add 的返回类型为 number
3. 联合类型与类型保护
联合类型表示一个值可以是几种类型之一。
类型保护是一些表达式,它们会在运行时检查以确保在某个作用域内的变量是某种特定类型。
// 联合类型
function printId(id: number | string) {
console.log(id);
}
// 类型保护 1: typeof
function printIdWithGuard(id: number | string) {
if (typeof id === "string") {
// 在这个块里,TypeScript 知道 id 是 string 类型
console.log(id.toUpperCase());
} else {
// 在这个块里,TypeScript 知道 id 是 number 类型
console.log(id.toFixed(2));
}
}
// 类型保护 2: instanceof
function logValue(x: Date | string) {
if (x instanceof Date) {
console.log(x.toUTCString());
} else {
console.log(x.toUpperCase());
}
}
4. 类型断言
类型断言是你告诉编译器“相信我,我知道自己在做什么”的一种方式,它不会进行类型的数据检查,只在编译阶段起作用。
有两种语法:
<>语法 (过时,不推荐在 JSX 中使用)as语法 (推荐)
// 假设我们有一个从 API 获取的 any 类型的数据 let someValue: any = "this is a string"; // 方式一:尖括号语法 let strLength1 = (<string>someValue).length; // 方式二:as 语法 (推荐) let strLength2 = (someValue as string).length; // 在 JSX 中必须使用 as 语法 const myElement = <div>Hello</div>; // const myElement2 = <div>Hello</div>; // tsx 文件,这样写会报错 const myElement2 = <div>Hello</div>; // 正确
5. 命名空间与模块
- 模块: 在现代 TypeScript/JavaScript 中,推荐使用模块(ES6 的
import/export)来组织代码,每个文件就是一个模块,通过export导出,通过import导入。 - 命名空间: 是一个较旧的(在 TypeScript 1.5 之前叫内部模块)组织代码的方式,它使用
namespace关键字,主要用于在单个文件内组织代码,或者在不支持模块的环境中组织代码,对于新项目,强烈推荐使用模块。
// math.ts (模块)
export function add(x: number, y: number): number {
return x + y;
}
export const PI = 3.14;
// main.ts (模块)
import { add, PI } from "./math";
console.log(add(1, 2)); // 3
console.log(PI); // 3.14
实践:与现有 JavaScript 项目集成
对于现有的 JS 项目,采用渐进式迁移策略是最佳实践。
-
安装依赖:
npm install --save-dev typescript @types/node ts-node
@types/node是 Node.js 的类型定义包,如果你使用其他库(如 React, Express),也需要安装对应的@types/库名包。 -
创建
tsconfig.json配置文件: 在项目根目录下运行:tsc --init
这会生成一个
tsconfig.json文件,你可以用它来配置 TypeScript 的编译选项。 -
关键配置选项:
"compilerOptions": { "target": "es6", "module": "commonjs", "strict": true, "outDir": "./dist", "rootDir": "./src" }target: 指定编译后的 JavaScript 版本。module: 指定模块系统。strict: 启用所有严格类型检查选项,推荐开启。outDir: 编译后文件的输出目录。rootDir: 源文件的根目录。
-
逐步迁移:
- 将
js文件重命名为ts或tsx。 - 从最简单的文件开始,为其添加类型注解。
- 对于暂时无法添加类型的代码,可以使用
any类型,或者通过// @ts-ignore来忽略特定行的错误。 - 使用
tsc -w命令来监视文件变化并自动编译。
- 将
最佳实践与工具链
- 使用
strict模式: 在tsconfig.json中开启"strict": true,它会强制你写出更安全的代码。 - 使用 ESLint 和 Prettier: 配合
@typescript-eslint/eslint-plugin和eslint-config-prettier,可以在编码规范和格式化方面与 TypeScript 完美集成。 - 利用 VS Code 的功能: 使用其强大的重构、跳转到定义、查看引用等功能。
- 为第三方库安装类型定义: 尽量为所有使用的库安装
@types/包名,如果没有官方类型,可以考虑自己编写或使用社区维护的类型。
学习资源总结
- 官方文档 (必读): TypeScript Handbook - 最权威、最全面的学习资料。
- 交互式教程: TypeScript for Node.js Developers - 官方提供的 5 分钟快速上手。
- 书籍:
- 《TypeScript Deep Dive》: 免费的在线书籍,内容非常深入。
- 《Programming TypeScript》: 一本很受欢迎的付费书籍。
- 视频课程:
在 Udemy, Coursera, Frontend Masters 等平台上有大量高质量的 TypeScript 课程。
- 社区:
希望这份详细的教程能帮助你顺利入门 TypeScript 并在实践中不断提升!祝你学习愉快!
