TypeScript从入门到精通

教程简介

TypeScript从入门到精通完整教程,涵盖基础类型、接口、泛型、类、装饰器、高级类型等核心概念,配有丰富代码示例和实战项目,适合JavaScript开发者进阶学习。

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的核心知识点:

  1. 基础类型系统:原始类型、数组、元组、枚举
  2. 接口与类型别名:定义复杂数据结构
  3. 函数:类型定义、重载、泛型函数
  4. 泛型:创建可复用的类型安全组件
  5. :面向对象编程、继承、抽象类
  6. 模块系统:ES模块、命名空间
  7. 装饰器:元编程能力
  8. 高级类型:条件类型、映射类型、模板字面量类型
  9. 实战项目:类型安全的API客户端

TypeScript正在成为前端和全栈开发的标准选择。掌握TypeScript不仅能提升代码质量,还能显著提高开发效率和团队协作体验。

本教程为原创内容,供学习参考使用。

内容声明

本文内容为AI技术学习教程,仅供学习参考。如涉及技术问题,欢迎通过 xurj005@163.com 与我们交流。

目录