TypeScript从入门到精通
本教程为原创内容,系统讲解TypeScript语言的核心概念与高级特性,适合JavaScript开发者进阶学习。
第一章:TypeScript概述
1.1 什么是TypeScript
TypeScript是由Microsoft开发和维护的开源编程语言。它是JavaScript的超集,在JavaScript的基础上添加了静态类型系统和其他高级特性。
1.2 为什么使用TypeScript
- 类型安全:在编译阶段就能发现错误
- 更好的IDE支持:自动补全、重构、跳转定义
- 代码可维护性:类型即文档,降低沟通成本
- 渐进式采用:可以逐步将现有JS项目迁移为TS
1.3 安装TypeScript
# 全局安装TypeScript编译器
npm install -g typescript
# 检查版本
tsc --version
# 编译TypeScript文件
tsc hello.ts
1.4 第一个TypeScript程序
// hello.ts
function greet(name: string): string {
return `Hello, ${name}! Welcome to TypeScript.`;
}
const message: string = greet("World");
console.log(message);
第二章:基础类型
2.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 greeting: string = `Hello, my name is ${color}`;
// null 和 undefined
let u: undefined = undefined;
let n: null = null;
// void
function warnUser(): void {
console.log("This is a warning message");
}
// never
function error(message: string): never {
throw new Error(message);
}
2.2 数组
// 方式一:类型[]
let numbers: number[] = [1, 2, 3, 4, 5];
// 方式二:Array<类型>
let names: Array<string> = ["Alice", "Bob", "Charlie"];
// 混合类型数组(使用联合类型)
let mixed: (string | number)[] = [1, "two", 3, "four"];
2.3 元组
// 元组:固定长度和类型的数组
let tuple: [string, number] = ["hello", 10];
// 访问元素
console.log(tuple[0].toUpperCase()); // OK
console.log(tuple[1].toFixed(2)); // OK
// 解构
let [first, second] = tuple;
console.log(first); // "hello"
console.log(second); // 10
// 可选元素
let optionalTuple: [string, number?] = ["hello"];
2.4 枚举
// 数字枚举
enum Direction {
Up = 1,
Down, // 自动为2
Left, // 自动为3
Right // 自动为4
}
let dir: Direction = Direction.Up;
console.log(dir); // 1
// 字符串枚举
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE"
}
// 反向映射(仅数字枚举)
enum Status {
Active,
Inactive
}
console.log(Status[0]); // "Active"
2.5 any、unknown与类型断言
// any:任意类型,关闭类型检查
let notSure: any = 4;
notSure = "maybe a string";
notSure = false;
// unknown:安全的any,使用前必须进行类型检查
let value: unknown = "Hello";
// value.toUpperCase(); // 错误!
if (typeof value === "string") {
console.log(value.toUpperCase()); // OK
}
// 类型断言
let someValue: unknown = "this is a string";
let strLength1: number = (someValue as string).length;
let strLength2: number = (<string>someValue).length;
第三章:接口与类型别名
3.1 接口基础
// 定义接口
interface User {
name: string;
age: number;
email: string;
}
// 使用接口
function printUser(user: User): void {
console.log(`Name: ${user.name}, Age: ${user.age}, Email: ${user.email}`);
}
const alice: User = {
name: "Alice",
age: 25,
email: "alice@example.com"
};
printUser(alice);
3.2 可选属性与只读属性
interface Config {
readonly host: string; // 只读,初始化后不可修改
port: number;
debug?: boolean; // 可选属性
ssl?: boolean;
}
const config: Config = {
host: "localhost",
port: 3000
};
// config.host = "other"; // 错误!只读属性
config.debug = true; // OK,可选属性可以赋值
3.3 函数类型接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(source: string, subString: string): boolean {
return source.search(subString) > -1;
};
console.log(mySearch("Hello World", "World")); // true
3.4 类型别名
// 基本类型别名
type ID = string | number;
// 对象类型别名
type Point = {
x: number;
y: number;
};
// 函数类型别名
type Callback = (data: string) => void;
// 联合类型
type Result = Success | Error;
// 交叉类型
type Employee = User & {
employeeId: number;
department: string;
};
3.5 接口 vs 类型别名
// 接口可以被extends和implements
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// 类型别名可以使用联合类型、交叉类型等高级特性
type StringOrNumber = string | number;
type Pair<T> = { first: T; second: T };
// 实际开发建议:
// - 定义对象结构用interface
// - 定义联合类型、交叉类型用type
第四章:函数
4.1 函数类型
// 完整的函数类型定义
function add(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: number, y: number): number {
return x + y;
};
4.2 可选参数与默认参数
// 可选参数(必须放在必需参数后面)
function buildName(firstName: string, lastName?: string): string {
if (lastName) {
return `${firstName} ${lastName}`;
}
return firstName;
}
console.log(buildName("Alice")); // "Alice"
console.log(buildName("Alice", "Smith")); // "Alice Smith"
// 默认参数
function createUser(name: string, role: string = "user", active: boolean = true): void {
console.log(`${name} - ${role} - ${active}`);
}
createUser("Alice"); // "Alice - user - true"
createUser("Bob", "admin"); // "Bob - admin - true"
createUser("Charlie", "editor", false); // "Charlie - editor - false"
4.3 剩余参数
function sum(...numbers: number[]): number {
return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4, 5)); // 15
// 与普通参数结合
function log(level: string, ...messages: string[]): void {
console.log(`[${level}]`, ...messages);
}
log("INFO", "Server started", "Port 3000");
4.4 函数重载
function reverse(value: string): string;
function reverse(value: number): number;
function reverse(value: string | number): string | number {
if (typeof value === "string") {
return value.split("").reverse().join("");
} else {
return Number(value.toString().split("").reverse().join(""));
}
}
console.log(reverse("hello")); // "olleh"
console.log(reverse(12345)); // 54321
第五章:泛型
5.1 泛型基础
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("hello"); // 明确指定类型
let output2 = identity(42); // 类型推断
// 泛型接口
interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
// 使用泛型接口
interface User {
id: number;
name: string;
}
let response: ApiResponse<User> = {
code: 200,
message: "success",
data: { id: 1, name: "Alice" }
};
5.2 泛型约束
// 使用extends约束泛型
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): number {
console.log(arg.length);
return arg.length;
}
logLength("hello"); // OK,string有length
logLength([1, 2, 3]); // OK,数组有length
logLength({ length: 10 }); // OK
// logLength(123); // 错误!number没有length
// keyof约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 25 };
console.log(getProperty(person, "name")); // "Alice"
// console.log(getProperty(person, "email")); // 错误!
5.3 泛型类
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
get size(): number {
return this.items.length;
}
}
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2
const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
5.4 常用工具类型
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial<T> - 所有属性变为可选
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string; age?: number; }
// Required<T> - 所有属性变为必需
type RequiredUser = Required<PartialUser>;
// Readonly<T> - 所有属性变为只读
type ReadonlyUser = Readonly<User>;
// Pick<T, K> - 选取部分属性
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string; }
// Omit<T, K> - 排除部分属性
type UserWithoutEmail = Omit<User, "email">;
// { id: number; name: string; age: number; }
// Record<K, V> - 构造键值对类型
type UserRoles = Record<string, string[]>;
const roles: UserRoles = {
admin: ["read", "write", "delete"],
user: ["read"]
};
第六章:类
6.1 类基础
class Person {
// 属性声明
name: string;
age: number;
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
greet(): string {
return `Hi, I'm ${this.name} and I'm ${this.age} years old.`;
}
}
const person = new Person("Alice", 25);
console.log(person.greet());
6.2 访问修饰符
class Employee {
public name: string; // 公开,任何地方都可访问
private salary: number; // 私有,仅类内部可访问
protected department: string; // 受保护,类内部和子类可访问
readonly id: number; // 只读,初始化后不可修改
constructor(name: string, salary: number, department: string, id: number) {
this.name = name;
this.salary = salary;
this.department = department;
this.id = id;
}
// getter
get info(): string {
return `${this.name} - ${this.department}`;
}
// setter
set updateSalary(value: number) {
if (value > 0) {
this.salary = value;
}
}
}
const emp = new Employee("Bob", 5000, "Engineering", 1);
console.log(emp.info); // "Bob - Engineering"
emp.updateSalary = 6000;
6.3 继承
class Animal {
constructor(public name: string) {}
move(distance: number): void {
console.log(`${this.name} moved ${distance} meters.`);
}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name);
}
bark(): void {
console.log("Woof! Woof!");
}
// 方法重写
move(distance: number = 5): void {
console.log("Running...");
super.move(distance);
}
}
const dog = new Dog("Buddy", "Golden Retriever");
dog.bark(); // "Woof! Woof!"
dog.move(10); // "Running..." "Buddy moved 10 meters."
6.4 抽象类
abstract class Shape {
abstract area(): number;
abstract perimeter(): number;
describe(): string {
return `Area: ${this.area()}, Perimeter: ${this.perimeter()}`;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
area(): number {
return Math.PI * this.radius ** 2;
}
perimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Rectangle extends Shape {
constructor(private width: number, private height: number) {
super();
}
area(): number {
return this.width * this.height;
}
perimeter(): number {
return 2 * (this.width + this.height);
}
}
// const shape = new Shape(); // 错误!不能实例化抽象类
const circle = new Circle(5);
console.log(circle.describe()); // "Area: 78.54, Perimeter: 31.42"
第七章:模块与命名空间
7.1 ES模块
// math.ts - 导出
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
export const PI = 3.14159;
// 默认导出
export default class Calculator {
// ...
}
// app.ts - 导入
import Calculator, { add, subtract, PI } from "./math";
import * as MathUtils from "./math";
console.log(add(1, 2));
console.log(MathUtils.PI);
7.2 命名空间
// Validation.ts
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
export class LettersOnlyValidator implements StringValidator {
isAcceptable(s: string): boolean {
return /^[A-Za-z]+$/.test(s);
}
}
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string): boolean {
return /^\d{5}$/.test(s);
}
}
}
// 使用
let validator = new Validation.LettersOnlyValidator();
console.log(validator.isAcceptable("Hello")); // true
第八章:装饰器
8.1 装饰器基础
// 装饰器是一个函数,可以修饰类、方法、属性等
// 类装饰器
function Logger(constructor: Function) {
console.log(`Creating instance of ${constructor.name}`);
}
@Logger
class MyService {
constructor() {
console.log("MyService created");
}
}
// 方法装饰器
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;
};
}
class MathService {
@Log
add(a: number, b: number): number {
return a + b;
}
}
const math = new MathService();
math.add(1, 2);
// Calling add with args: [1, 2]
// Result: 3
8.2 装饰器工厂
// 带参数的装饰器
function Deprecated(message: string) {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.warn(`Warning: ${propertyKey} is deprecated. ${message}`);
return originalMethod.apply(this, args);
};
};
}
class UserService {
@Deprecated("Use getUserById instead")
findUser(id: number) {
// ...
}
getUserById(id: number) {
// ...
}
}
第九章:高级类型
9.1 类型守卫
// typeof守卫
function padLeft(value: string | number, padding: string | number): string {
if (typeof padding === "number") {
return " ".repeat(padding) + value;
}
return padding + value;
}
// instanceof守卫
class Bird {
fly() { console.log("Flying"); }
}
class Fish {
swim() { console.log("Swimming"); }
}
function move(animal: Bird | Fish) {
if (animal instanceof Bird) {
animal.fly();
} else {
animal.swim();
}
}
// 自定义类型守卫
function isBird(animal: Bird | Fish): animal is Bird {
return (animal as Bird).fly !== undefined;
}
9.2 条件类型
// 基本条件类型
type IsString<T> = T extends string ? "yes" : "no";
type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"
// infer关键字
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Fn = () => string;
type Result = ReturnType<Fn>; // string
// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;
type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]
9.3 映射类型
// 自定义映射类型
type Optional<T> = {
[K in keyof T]?: T[K];
};
type Readonly2<T> = {
readonly [K in keyof T]: T[K];
};
// 结合条件类型
type Nullable<T> = {
[K in keyof T]: T[K] | null;
};
interface User {
name: string;
age: number;
}
type NullableUser = Nullable<User>;
// { name: string | null; age: number | null; }
9.4 模板字面量类型
type Color = "red" | "blue" | "green";
type Size = "small" | "medium" | "large";
// 组合字面量类型
type ColorSize = `${Color}-${Size}`;
// "red-small" | "red-medium" | "red-large" | "blue-small" | ...
// 实际应用
type CSSProperty = "margin" | "padding";
type Direction = "top" | "right" | "bottom" | "left";
type CSSDirectionalProperty = `${CSSProperty}-${Direction}`;
// "margin-top" | "margin-right" | ... | "padding-left"
第十章:实战项目 - 类型安全的API客户端
10.1 项目结构
// types/api.ts
export interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
export interface PaginatedData<T> {
list: T[];
total: number;
page: number;
pageSize: number;
}
export interface User {
id: number;
name: string;
email: string;
role: "admin" | "user" | "editor";
}
export interface Post {
id: number;
title: string;
content: string;
authorId: number;
createdAt: string;
}
export interface Comment {
id: number;
postId: number;
userId: number;
content: string;
}
10.2 API客户端实现
// api/client.ts
import { ApiResponse, PaginatedData } from "../types/api";
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
interface RequestConfig {
method: HttpMethod;
url: string;
data?: unknown;
params?: Record<string, string | number>;
}
class ApiClient {
private baseUrl: string;
private headers: Record<string, string>;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
this.headers = {
"Content-Type": "application/json"
};
}
setToken(token: string): void {
this.headers["Authorization"] = `Bearer ${token}`;
}
private async request<T>(config: RequestConfig): Promise<ApiResponse<T>> {
let url = `${this.baseUrl}${config.url}`;
if (config.params) {
const searchParams = new URLSearchParams();
Object.entries(config.params).forEach(([key, value]) => {
searchParams.append(key, String(value));
});
url += `?${searchParams.toString()}`;
}
const response = await fetch(url, {
method: config.method,
headers: this.headers,
body: config.data ? JSON.stringify(config.data) : undefined
});
if (!response.ok) {
throw new Error(`HTTP Error: ${response.status}`);
}
return response.json();
}
async get<T>(url: string, params?: Record<string, string | number>): Promise<ApiResponse<T>> {
return this.request<T>({ method: "GET", url, params });
}
async post<T>(url: string, data?: unknown): Promise<ApiResponse<T>> {
return this.request<T>({ method: "POST", url, data });
}
async put<T>(url: string, data?: unknown): Promise<ApiResponse<T>> {
return this.request<T>({ method: "PUT", url, data });
}
async delete<T>(url: string): Promise<ApiResponse<T>> {
return this.request<T>({ method: "DELETE", url });
}
}
export default ApiClient;
10.3 资源服务封装
// api/resources.ts
import ApiClient from "./client";
import { ApiResponse, PaginatedData } from "../types/api";
class ResourceService<T extends { id: number }> {
constructor(
private client: ApiClient,
private endpoint: string
) {}
async getAll(page: number = 1, pageSize: number = 10): Promise<ApiResponse<PaginatedData<T>>> {
return this.client.get<PaginatedData<T>>(this.endpoint, { page, pageSize });
}
async getById(id: number): Promise<ApiResponse<T>> {
return this.client.get<T>(`${this.endpoint}/${id}`);
}
async create(data: Omit<T, "id">): Promise<ApiResponse<T>> {
return this.client.post<T>(this.endpoint, data);
}
async update(id: number, data: Partial<T>): Promise<ApiResponse<T>> {
return this.client.put<T>(`${this.endpoint}/${id}`, data);
}
async delete(id: number): Promise<ApiResponse<void>> {
return this.client.delete<void>(`${this.endpoint}/${id}`);
}
}
export default ResourceService;
10.4 使用示例
// main.ts
import ApiClient from "./api/client";
import ResourceService from "./api/resources";
import { User, Post } from "./types/api";
const client = new ApiClient("https://api.example.com");
client.setToken("your-token-here");
const userService = new ResourceService<User>(client, "/users");
const postService = new ResourceService<Post>(client, "/posts");
async function main() {
// 获取用户列表
const usersResponse = await userService.getAll(1, 20);
console.log(`Total users: ${usersResponse.data.total}`);
usersResponse.data.list.forEach(user => {
console.log(`${user.name} (${user.role})`);
});
// 创建新用户
const newUser = await userService.create({
name: "Charlie",
email: "charlie@example.com",
role: "user"
});
console.log(`Created user with id: ${newUser.data.id}`);
// 获取文章并按作者过滤
const posts = await postService.getAll();
const alicePosts = posts.data.list.filter(post => post.authorId === 1);
console.log(`Alice has ${alicePosts.length} posts`);
}
main().catch(console.error);
总结
本教程涵盖了TypeScript的核心知识点:
- 基础类型系统:原始类型、数组、元组、枚举
- 接口与类型别名:定义复杂数据结构
- 函数:类型定义、重载、泛型函数
- 泛型:创建可复用的类型安全组件
- 类:面向对象编程、继承、抽象类
- 模块系统:ES模块、命名空间
- 装饰器:元编程能力
- 高级类型:条件类型、映射类型、模板字面量类型
- 实战项目:类型安全的API客户端
TypeScript正在成为前端和全栈开发的标准选择。掌握TypeScript不仅能提升代码质量,还能显著提高开发效率和团队协作体验。
本教程为原创内容,供学习参考使用。