TypeScript 入门教程:JavaScript 的超集
16 min read
#TypeScript#JavaScript#类型系统#前端开发
TypeScript 入门教程:JavaScript 的超集
TypeScript 是 JavaScript 的超集,为 JavaScript 添加了静态类型系统。它由 Microsoft 开发和维护,已成为现代前端开发的标准选择。
为什么使用 TypeScript?
- 类型安全:在编译时捕获错误,而不是运行时
- 更好的 IDE 支持:智能提示、自动补全、重构工具
- 代码可维护性:类型注解作为文档,提高代码可读性
- 现代 JavaScript 特性:支持最新的 ECMAScript 特性
- 渐进式采用:可以逐步将 JavaScript 项目迁移到 TypeScript
- 强大的生态系统:大量类型定义库(@types)
环境准备
安装 TypeScript
# 全局安装
npm install -g typescript
# 检查版本
tsc --version
# 项目中安装
npm install --save-dev typescript
初始化 TypeScript 项目
# 创建 tsconfig.json 配置文件
tsc --init
编译 TypeScript
# 编译单个文件
tsc hello.ts
# 编译整个项目
tsc
# 监听模式(自动编译)
tsc --watch
基础类型
1. 基本类型
// 布尔类型
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 fullName: string = `Bob Smith`;
let sentence: string = `Hello, my name is ${fullName}`;
// 数组类型
let list1: number[] = [1, 2, 3];
let list2: Array<number> = [1, 2, 3];
// 元组类型(固定长度和类型的数组)
let tuple: [string, number];
tuple = ["hello", 10]; // ✅ 正确
// tuple = [10, "hello"]; // ❌ 错误
// 枚举类型
enum Color {
Red,
Green,
Blue
}
let c: Color = Color.Green;
// 字符串枚举
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT"
}
// Any 类型(任意类型,失去类型检查)
let notSure: any = 4;
notSure = "maybe a string";
notSure = false;
// Unknown 类型(类型安全的 any)
let value: unknown = 4;
// value.toFixed(); // ❌ 错误:需要类型检查
if (typeof value === "number") {
value.toFixed(); // ✅ 正确
}
// Void 类型(无返回值)
function warnUser(): void {
console.log("This is a warning message");
}
// Null 和 Undefined
let u: undefined = undefined;
let n: null = null;
// Never 类型(永不返回)
function error(message: string): never {
throw new Error(message);
}
function infiniteLoop(): never {
while (true) {}
}
2. 类型断言
// 尖括号语法
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
// as 语法(推荐,在 JSX 中必须使用)
let someValue2: any = "this is a string";
let strLength2: number = (someValue2 as string).length;
// 非空断言操作符(!)
function processValue(value: string | null) {
// 告诉编译器 value 不为 null
console.log(value!.toUpperCase());
}
3. 联合类型和交叉类型
// 联合类型(可以是多种类型之一)
let value: string | number;
value = "hello"; // ✅
value = 42; // ✅
// value = true; // ❌
function printId(id: string | number) {
if (typeof id === "string") {
console.log(id.toUpperCase());
} else {
console.log(id.toFixed(2));
}
}
// 交叉类型(组合多个类型)
interface Person {
name: string;
}
interface Employee {
employeeId: number;
}
type Staff = Person & Employee;
const staff: Staff = {
name: "Alice",
employeeId: 12345
};
4. 字面量类型
// 字符串字面量类型
let direction: "left" | "right" | "up" | "down";
direction = "left"; // ✅
// direction = "forward"; // ❌
// 数字字面量类型
let diceRoll: 1 | 2 | 3 | 4 | 5 | 6;
// 布尔字面量类型
let success: true;
success = true; // ✅
// success = false; // ❌
// 混合字面量类型
type Status = "success" | "error" | "loading" | 200 | 404 | 500;
接口(Interface)
接口用于定义对象的形状:
// 基本接口
interface User {
name: string;
age: number;
}
const user: User = {
name: "Alice",
age: 25
};
// 可选属性
interface Person {
name: string;
age?: number; // 可选
}
const person1: Person = { name: "Bob" }; // ✅
const person2: Person = { name: "Alice", age: 30 }; // ✅
// 只读属性
interface Point {
readonly x: number;
readonly y: number;
}
const point: Point = { x: 10, y: 20 };
// point.x = 5; // ❌ 错误:只读属性
// 函数类型接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
const mySearch: SearchFunc = function(src, sub) {
return src.indexOf(sub) > -1;
};
// 索引签名
interface StringArray {
[index: number]: string;
}
const myArray: StringArray = ["Bob", "Alice"];
interface StringDictionary {
[key: string]: string;
}
const dict: StringDictionary = {
name: "Alice",
city: "Beijing"
};
// 接口继承
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
const square: Square = {
color: "blue",
sideLength: 10
};
// 多重继承
interface PenStroke {
penWidth: number;
}
interface ColoredSquare extends Shape, PenStroke {
sideLength: number;
}
函数
1. 函数类型
// 函数声明
function add(x: number, y: number): number {
return x + y;
}
// 函数表达式
const subtract = function(x: number, y: number): number {
return x - y;
};
// 箭头函数
const multiply = (x: number, y: number): number => x * y;
// 完整的函数类型
let myAdd: (x: number, y: number) => number = function(x, y) {
return x + y;
};
// 可选参数
function buildName(firstName: string, lastName?: string): string {
if (lastName) {
return `${firstName} ${lastName}`;
}
return firstName;
}
// 默认参数
function greet(name: string, greeting: string = "Hello"): string {
return `${greeting}, ${name}!`;
}
// 剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 函数重载
function reverse(x: string): string;
function reverse(x: number): number;
function reverse(x: string | number): string | number {
if (typeof x === "string") {
return x.split("").reverse().join("");
}
return Number(x.toString().split("").reverse().join(""));
}
console.log(reverse("hello")); // "olleh"
console.log(reverse(12345)); // 54321
2. 泛型函数
// 基本泛型函数
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("myString");
let output2 = identity<number>(42);
// 泛型数组
function loggingIdentity<T>(arg: T[]): T[] {
console.log(arg.length);
return arg;
}
// 泛型约束
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("hello"); // ✅
logLength([1, 2, 3]); // ✅
logLength({ length: 10, value: 3 }); // ✅
// logLength(3); // ❌ 错误:number 没有 length 属性
类(Class)
// 基本类
class Person {
// 属性
name: string;
age: number;
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
greet(): void {
console.log(`Hello, I'm ${this.name}`);
}
}
const person = new Person("Alice", 25);
person.greet();
// 访问修饰符
class Animal {
public 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 getAge(): number {
return this.age;
}
}
// 简化的构造函数
class Dog {
constructor(
public name: string,
private age: number
) {}
bark(): void {
console.log(`${this.name} says Woof!`);
}
}
// 继承
class Animal2 {
constructor(public name: string) {}
move(distance: number = 0): void {
console.log(`${this.name} moved ${distance}m`);
}
}
class Dog2 extends Animal2 {
bark(): void {
console.log("Woof! Woof!");
}
}
const dog = new Dog2("Buddy");
dog.bark();
dog.move(10);
// 抽象类
abstract class Shape {
abstract getArea(): number;
printArea(): void {
console.log(`Area: ${this.getArea()}`);
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
}
const circle = new Circle(5);
circle.printArea();
// 静态成员
class MathUtils {
static PI: number = 3.14159;
static calculateCircumference(diameter: number): number {
return diameter * MathUtils.PI;
}
}
console.log(MathUtils.PI);
console.log(MathUtils.calculateCircumference(10));
// Getter 和 Setter
class Employee {
private _salary: number = 0;
get salary(): number {
return this._salary;
}
set salary(value: number) {
if (value < 0) {
throw new Error("Salary cannot be negative");
}
this._salary = value;
}
}
const emp = new Employee();
emp.salary = 50000;
console.log(emp.salary);
泛型(Generics)
// 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
constructor(zeroValue: T, addFn: (x: T, y: T) => T) {
this.zeroValue = zeroValue;
this.add = addFn;
}
}
const myGenericNumber = new GenericNumber<number>(0, (x, y) => x + y);
// 泛型接口
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
// 泛型约束
interface HasLength {
length: number;
}
function getLength<T extends HasLength>(arg: T): number {
return arg.length;
}
getLength("hello"); // ✅
getLength([1, 2, 3]); // ✅
// getLength(123); // ❌
// 在泛型约束中使用类型参数
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person3 = { name: "Alice", age: 25 };
getProperty(person3, "name"); // ✅
// getProperty(person3, "email"); // ❌
高级类型
1. 类型别名
// 基本类型别名
type Name = string;
type Age = number;
type User = {
name: Name;
age: Age;
};
// 联合类型别名
type ID = string | number;
// 函数类型别名
type GreetFunction = (name: string) => string;
const greet: GreetFunction = (name) => `Hello, ${name}!`;
2. 映射类型
// Partial - 所有属性变为可选
interface Todo {
title: string;
description: string;
completed: boolean;
}
type PartialTodo = Partial<Todo>;
// 等价于:
// {
// title?: string;
// description?: string;
// completed?: boolean;
// }
// Required - 所有属性变为必需
type RequiredTodo = Required<PartialTodo>;
// Readonly - 所有属性变为只读
type ReadonlyTodo = Readonly<Todo>;
// Pick - 选择部分属性
type TodoPreview = Pick<Todo, "title" | "completed">;
// Omit - 排除部分属性
type TodoInfo = Omit<Todo, "completed">;
// Record - 创建键值对类型
type PageInfo = {
title: string;
};
type Pages = "home" | "about" | "contact";
type PageMap = Record<Pages, PageInfo>;
const pages: PageMap = {
home: { title: "Home" },
about: { title: "About" },
contact: { title: "Contact" }
};
3. 条件类型
// 基本条件类型
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>; // string
type D = NonNullable<number | undefined>; // number
// Extract - 提取可赋值的类型
type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// Exclude - 排除可赋值的类型
type T2 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
模块
// 导出
// math.ts
export function add(x: number, y: number): number {
return x + y;
}
export function subtract(x: number, y: number): number {
return x - y;
}
export const PI = 3.14159;
// 默认导出
export default class Calculator {
add(x: number, y: number): number {
return x + y;
}
}
// 导入
// main.ts
import { add, subtract, PI } from "./math";
import Calculator from "./math";
// 重命名导入
import { add as addition } from "./math";
// 导入所有
import * as math from "./math";
// 命名空间(不推荐,使用模块代替)
namespace Validation {
export interface StringValidator {
isValid(s: string): boolean;
}
export class EmailValidator implements StringValidator {
isValid(s: string): boolean {
return s.includes("@");
}
}
}
const validator = new Validation.EmailValidator();
装饰器(Decorators)
装饰器是一种特殊类型的声明,可以附加到类、方法、访问器、属性或参数上。
// 启用装饰器需要在 tsconfig.json 中设置:
// "experimentalDecorators": true
// 类装饰器
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
}
// 方法装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with args:`, args);
const result = originalMethod.apply(this, args);
console.log(`Result:`, result);
return result;
};
return descriptor;
}
class Calculator2 {
@log
add(a: number, b: number): number {
return a + b;
}
}
// 属性装饰器
function readonly(target: any, propertyKey: string) {
Object.defineProperty(target, propertyKey, {
writable: false
});
}
class Person2 {
@readonly
name: string = "Alice";
}
实战示例:Todo 应用
// 类型定义
interface Todo {
id: number;
title: string;
completed: boolean;
createdAt: Date;
}
type TodoInput = Omit<Todo, "id" | "createdAt">;
// Todo 管理类
class TodoManager {
private todos: Todo[] = [];
private nextId: number = 1;
// 添加 Todo
addTodo(input: TodoInput): Todo {
const todo: Todo = {
id: this.nextId++,
title: input.title,
completed: input.completed,
createdAt: new Date()
};
this.todos.push(todo);
return todo;
}
// 获取所有 Todo
getAllTodos(): Todo[] {
return [...this.todos];
}
// 根据 ID 获取 Todo
getTodoById(id: number): Todo | undefined {
return this.todos.find(todo => todo.id === id);
}
// 更新 Todo
updateTodo(id: number, updates: Partial<TodoInput>): Todo | undefined {
const todo = this.getTodoById(id);
if (todo) {
Object.assign(todo, updates);
return todo;
}
return undefined;
}
// 删除 Todo
deleteTodo(id: number): boolean {
const index = this.todos.findIndex(todo => todo.id === id);
if (index !== -1) {
this.todos.splice(index, 1);
return true;
}
return false;
}
// 切换完成状态
toggleTodo(id: number): Todo | undefined {
const todo = this.getTodoById(id);
if (todo) {
todo.completed = !todo.completed;
return todo;
}
return undefined;
}
// 获取未完成的 Todo
getIncompleteTodos(): Todo[] {
return this.todos.filter(todo => !todo.completed);
}
// 获取已完成的 Todo
getCompletedTodos(): Todo[] {
return this.todos.filter(todo => todo.completed);
}
}
// 使用示例
const manager = new TodoManager();
manager.addTodo({ title: "学习 TypeScript", completed: false });
manager.addTodo({ title: "完成项目", completed: false });
manager.addTodo({ title: "写文档", completed: true });
console.log("所有 Todo:", manager.getAllTodos());
console.log("未完成:", manager.getIncompleteTodos());
console.log("已完成:", manager.getCompletedTodos());
manager.toggleTodo(1);
manager.updateTodo(2, { title: "完成重要项目" });
manager.deleteTodo(3);
配置文件(tsconfig.json)
{
"compilerOptions": {
// 目标 JavaScript 版本
"target": "ES2020",
// 模块系统
"module": "commonjs",
// 输出目录
"outDir": "./dist",
// 源码目录
"rootDir": "./src",
// 严格模式
"strict": true,
// 启用所有严格类型检查选项
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
// 模块解析
"moduleResolution": "node",
"esModuleInterop": true,
// 生成声明文件
"declaration": true,
// 生成 source map
"sourceMap": true,
// 允许导入 JSON
"resolveJsonModule": true,
// 跳过库检查
"skipLibCheck": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
最佳实践
- 启用严格模式:在 tsconfig.json 中设置
"strict": true - 避免使用 any:尽可能使用具体类型或 unknown
- 使用接口定义对象结构:提高代码可读性
- 利用类型推断:让 TypeScript 自动推断类型
- 使用联合类型而非枚举:更灵活
- 编写类型守卫:安全地缩小类型范围
- 使用工具类型:Partial、Required、Pick、Omit 等
- 为第三方库安装类型定义:
npm install @types/库名
下一步学习
- 高级类型:映射类型、条件类型、模板字面量类型
- 装饰器:类装饰器、方法装饰器、属性装饰器
- 与框架集成:React + TypeScript、Vue + TypeScript
- 类型体操:复杂类型推导和转换
- 编译器 API:自定义 TypeScript 工具
总结
通过本教程,你已经掌握了 TypeScript 的核心知识:
- ✅ 基础类型系统
- ✅ 接口和类型别名
- ✅ 函数和泛型
- ✅ 类和继承
- ✅ 高级类型特性
- ✅ 模块系统
- ✅ 装饰器
TypeScript 让你的 JavaScript 代码更安全、更易维护。继续实践,你将能够构建大型、可靠的应用程序!