TypeScript_学习资料_基础类型系统

33 阅读8分钟

TypeScript 基础类型系统详解

目录

  1. TypeScript 概述
  2. 原始类型详解
  3. 数组和元组类型
  4. 枚举类型深入
  5. 对象类型
  6. 函数类型
  7. 联合类型和交叉类型
  8. 字面量类型
  9. 类型断言
  10. 实践练习

TypeScript 概述

什么是 TypeScript?

TypeScript 是 Microsoft 开发的开源编程语言,它是 JavaScript 的一个超集,添加了静态类型定义。TypeScript 代码会被编译成纯 JavaScript 代码,可以在任何支持 JavaScript 的环境中运行。

核心优势

  1. 静态类型检查:在编译时发现错误,而不是运行时
  2. 更好的 IDE 支持:自动完成、重构、导航等功能
  3. 代码可读性:类型注解使代码意图更清晰
  4. 团队协作:类型定义作为代码契约,减少沟通成本
  5. 渐进式采用:可以逐步将 JavaScript 项目迁移到 TypeScript

环境搭建

# 全局安装 TypeScript
npm install -g typescript

# 检查版本
tsc --version

# 初始化 TypeScript 项目
tsc --init

# 编译 TypeScript 文件
tsc filename.ts

# 监听模式编译
tsc --watch

原始类型详解

1. 字符串类型 (string)

// 基本字符串
let firstName: string = "张";
let lastName: string = '三';

// 模板字符串
let fullName: string = `${firstName}${lastName}`;
let greeting: string = `Hello, ${fullName}!`;

// 多行字符串
let multiLine: string = `
    这是一个
    多行字符串
    示例
`;

// 字符串方法的类型推断
let message: string = "Hello World";
let upperMessage: string = message.toUpperCase(); // TypeScript 知道这返回 string
let messageLength: number = message.length;       // TypeScript 知道这返回 number

2. 数字类型 (number)

// 十进制
let decimal: number = 6;
let float: number = 3.14;
let negative: number = -42;

// 十六进制
let hex: number = 0xf00d;

// 二进制 (ES2015)
let binary: number = 0b1010;

// 八进制 (ES2015)
let octal: number = 0o744;

// 大数字 (ES2020)
let bigInt: bigint = 100n;

// 特殊数值
let notANumber: number = NaN;
let infinity: number = Infinity;
let negativeInfinity: number = -Infinity;

// 数字方法的类型推断
let num: number = 123.456;
let rounded: number = num.toFixed(2); // 注意:toFixed 返回 string,这里会报错
let correctRounded: string = num.toFixed(2); // 正确的类型

3. 布尔类型 (boolean)

// 基本布尔值
let isDone: boolean = false;
let isActive: boolean = true;

// 布尔表达式
let isGreater: boolean = 10 > 5;
let isEqual: boolean = "hello" === "world";

// 逻辑运算
let result1: boolean = true && false;
let result2: boolean = true || false;
let result3: boolean = !true;

// 条件判断中的类型推断
function checkStatus(isActive: boolean): string {
    if (isActive) {
        return "激活状态";
    } else {
        return "非激活状态";
    }
}

4. null 和 undefined

// 基本用法
let u: undefined = undefined;
let n: null = null;

// 在严格模式下,null 和 undefined 只能赋值给自己或 void
let strictUndefined: undefined = undefined;
let strictNull: null = null;

// 联合类型中的使用
let nullable: string | null = null;
let optional: string | undefined = undefined;

// 可选参数和属性
function greet(name?: string): string {
    if (name === undefined) {
        return "Hello, stranger!";
    }
    return `Hello, ${name}!`;
}

interface User {
    name: string;
    email?: string; // 等同于 email: string | undefined
}

5. void 类型

// 函数没有返回值
function logMessage(message: string): void {
    console.log(message);
    // 隐式返回 undefined
}

// 显式返回 undefined
function doNothing(): void {
    return undefined;
}

// void 类型的变量只能赋值为 undefined 或 null(在非严格模式下)
let unusable: void = undefined;

6. never 类型

// 永远不会返回的函数
function error(message: string): never {
    throw new Error(message);
}

// 无限循环
function infiniteLoop(): never {
    while (true) {
        // 无限循环
    }
}

// never 类型在联合类型中会被忽略
type Example = string | number | never; // 等同于 string | number

// 用于详尽性检查
type Shape = "circle" | "square" | "triangle";

function getArea(shape: Shape): number {
    switch (shape) {
        case "circle":
            return Math.PI * 1 * 1;
        case "square":
            return 1 * 1;
        case "triangle":
            return 0.5 * 1 * 1;
        default:
            // 如果 Shape 类型添加了新值但这里没有处理,TypeScript 会报错
            const exhaustiveCheck: never = shape;
            return exhaustiveCheck;
    }
}

数组和元组类型

1. 数组类型的定义方式

// 方式一:类型[]
let numbers: number[] = [1, 2, 3, 4, 5];
let strings: string[] = ["hello", "world"];
let booleans: boolean[] = [true, false, true];

// 方式二:Array<类型>
let numbersGeneric: Array<number> = [1, 2, 3, 4, 5];
let stringsGeneric: Array<string> = ["hello", "world"];

// 混合类型数组
let mixed: (string | number)[] = ["hello", 42, "world", 100];
let mixedGeneric: Array<string | number> = ["hello", 42, "world", 100];

2. 数组操作和类型推断

let fruits: string[] = ["apple", "banana", "orange"];

// 数组方法的类型推断
let firstFruit: string | undefined = fruits[0]; // 可能是 undefined
let fruitCount: number = fruits.length;
let upperFruits: string[] = fruits.map(fruit => fruit.toUpperCase());
let longFruits: string[] = fruits.filter(fruit => fruit.length > 5);

// 数组解构
let [first, second, ...rest] = fruits;
// first: string | undefined
// second: string | undefined  
// rest: string[]

// 添加元素
fruits.push("grape"); // ✅ 正确
// fruits.push(123); // ❌ 错误:类型不匹配

3. 只读数组

// ReadonlyArray 类型
let readonlyNumbers: ReadonlyArray<number> = [1, 2, 3, 4, 5];
let readonlyStrings: readonly string[] = ["hello", "world"];

// 只读数组不能修改
// readonlyNumbers.push(6); // ❌ 错误:push 方法不存在
// readonlyNumbers[0] = 10; // ❌ 错误:不能赋值

// 但可以读取
let firstNumber: number = readonlyNumbers[0];
let numbersLength: number = readonlyNumbers.length;

// 可以通过类型断言转换为可变数组
let mutableNumbers: number[] = readonlyNumbers as number[];

4. 元组类型详解

// 基本元组
let person: [string, number] = ["张三", 30];
let coordinate: [number, number] = [10, 20];

// 访问元组元素
let name: string = person[0];
let age: number = person[1];
// let invalid = person[2]; // ❌ 错误:索引超出范围

// 元组解构
let [personName, personAge] = person;

// 可选元组元素
let optionalTuple: [string, number?] = ["hello"];
let optionalTuple2: [string, number?] = ["hello", 42];

// 剩余元素
let restTuple: [string, ...number[]] = ["hello", 1, 2, 3, 4];
let namedRestTuple: [name: string, ...scores: number[]] = ["张三", 90, 85, 92];

// 只读元组
let readonlyTuple: readonly [string, number] = ["hello", 42];
// readonlyTuple[0] = "world"; // ❌ 错误:只读

5. 复杂数组类型示例

// 对象数组
interface User {
    id: number;
    name: string;
    email: string;
}

let users: User[] = [
    { id: 1, name: "张三", email: "zhangsan@example.com" },
    { id: 2, name: "李四", email: "lisi@example.com" }
];

// 函数数组
type MathOperation = (a: number, b: number) => number;
let operations: MathOperation[] = [
    (a, b) => a + b,
    (a, b) => a - b,
    (a, b) => a * b,
    (a, b) => a / b
];

// 嵌套数组
let matrix: number[][] = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
];

// 元组数组
let points: [number, number][] = [
    [0, 0],
    [10, 10],
    [20, 30]
];

枚举类型深入

1. 数字枚举

// 基本数字枚举
enum Direction {
    Up,    // 0
    Down,  // 1
    Left,  // 2
    Right  // 3
}

// 使用枚举
let dir: Direction = Direction.Up;
console.log(dir); // 输出: 0
console.log(Direction[0]); // 输出: "Up" (反向映射)

// 自定义起始值
enum Status {
    Pending = 1,
    Approved, // 2
    Rejected  // 3
}

// 自定义所有值
enum HttpStatus {
    OK = 200,
    NotFound = 404,
    InternalServerError = 500
}

2. 字符串枚举

// 字符串枚举
enum Color {
    Red = "red",
    Green = "green",
    Blue = "blue"
}

// 使用字符串枚举
let favoriteColor: Color = Color.Red;
console.log(favoriteColor); // 输出: "red"

// 字符串枚举没有反向映射
// console.log(Color["red"]); // ❌ 错误

// 实际应用示例
enum LogLevel {
    ERROR = "error",
    WARN = "warn", 
    INFO = "info",
    DEBUG = "debug"
}

function log(level: LogLevel, message: string): void {
    console.log(`[${level.toUpperCase()}] ${message}`);
}

log(LogLevel.INFO, "应用启动成功");

3. 异构枚举(不推荐)

// 混合数字和字符串(不推荐使用)
enum Mixed {
    No = 0,
    Yes = "YES"
}

4. 常量枚举

// 常量枚举在编译时会被内联
const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [
    Directions.Up,
    Directions.Down,
    Directions.Left,
    Directions.Right
];

// 编译后的 JavaScript:
// let directions = [0, 1, 2, 3];

5. 枚举的高级用法

// 计算成员
enum FileAccess {
    None,
    Read = 1 << 1,     // 2
    Write = 1 << 2,    // 4
    ReadWrite = Read | Write, // 6
}

// 枚举作为类型
function processStatus(status: HttpStatus): string {
    switch (status) {
        case HttpStatus.OK:
            return "请求成功";
        case HttpStatus.NotFound:
            return "资源未找到";
        case HttpStatus.InternalServerError:
            return "服务器内部错误";
        default:
            return "未知状态";
    }
}

// 枚举的 keyof 操作
type StatusKeys = keyof typeof HttpStatus; // "OK" | "NotFound" | "InternalServerError"

// 获取枚举的所有值
function getAllStatusValues(): HttpStatus[] {
    return Object.values(HttpStatus);
}

对象类型

1. 对象类型的基本定义

// 内联对象类型
let user: { name: string; age: number; email: string } = {
    name: "张三",
    age: 30,
    email: "zhangsan@example.com"
};

// 可选属性
let partialUser: { name: string; age?: number } = {
    name: "李四"
    // age 是可选的
};

// 只读属性
let readonlyUser: { readonly id: number; name: string } = {
    id: 1,
    name: "王五"
};
// readonlyUser.id = 2; // ❌ 错误:只读属性

2. 索引签名

// 字符串索引签名
interface StringDictionary {
    [key: string]: string;
}

let dict: StringDictionary = {
    "name": "张三",
    "city": "北京",
    "country": "中国"
};

// 数字索引签名
interface NumberArray {
    [index: number]: string;
}

let arr: NumberArray = ["first", "second", "third"];

// 混合索引签名
interface MixedDictionary {
    [key: string]: string | number;
    [index: number]: string; // 数字索引的类型必须是字符串索引类型的子类型
    length: number; // 具体属性
}

3. 嵌套对象类型

interface Address {
    street: string;
    city: string;
    zipCode: string;
}

interface Person {
    name: string;
    age: number;
    address: Address;
    contacts: {
        email: string;
        phone?: string;
    };
}

let person: Person = {
    name: "张三",
    age: 30,
    address: {
        street: "长安街1号",
        city: "北京",
        zipCode: "100000"
    },
    contacts: {
        email: "zhangsan@example.com"
    }
};

函数类型

1. 函数类型的定义

// 函数声明
function add(a: number, b: number): number {
    return a + b;
}

// 函数表达式
let multiply = function(a: number, b: number): number {
    return a * b;
};

// 箭头函数
let divide = (a: number, b: number): number => a / b;

// 完整的函数类型
let subtract: (a: number, b: number) => number = function(a, b) {
    return a - b;
};

2. 可选参数和默认参数

// 可选参数
function greet(firstName: string, lastName?: string): string {
    if (lastName) {
        return `Hello, ${firstName} ${lastName}!`;
    }
    return `Hello, ${firstName}!`;
}

greet("张三");        // ✅ 正确
greet("张", "三");    // ✅ 正确

// 默认参数
function createUser(name: string, age: number = 18, role: string = "user"): object {
    return { name, age, role };
}

createUser("张三");                    // age=18, role="user"
createUser("李四", 25);               // role="user"
createUser("王五", 30, "admin");      // 所有参数都指定

3. 剩余参数

// 剩余参数
function sum(first: number, ...rest: number[]): number {
    return first + rest.reduce((a, b) => a + b, 0);
}

sum(1);           // 1
sum(1, 2, 3, 4);  // 10

// 剩余参数的类型推断
function logMessages(prefix: string, ...messages: string[]): void {
    messages.forEach(message => {
        console.log(`${prefix}: ${message}`);
    });
}

logMessages("INFO", "应用启动", "数据库连接成功", "服务器就绪");

4. 函数重载

// 函数重载声明
function processData(data: string): string;
function processData(data: number): number;
function processData(data: boolean): boolean;

// 函数实现
function processData(data: string | number | boolean): string | number | boolean {
    if (typeof data === "string") {
        return data.toUpperCase();
    } else if (typeof data === "number") {
        return data * 2;
    } else {
        return !data;
    }
}

// 使用
let result1 = processData("hello");  // string
let result2 = processData(42);       // number
let result3 = processData(true);     // boolean

5. 高阶函数类型

// 接受函数作为参数
function applyOperation(
    a: number, 
    b: number, 
    operation: (x: number, y: number) => number
): number {
    return operation(a, b);
}

// 使用
let result = applyOperation(10, 5, (x, y) => x + y); // 15

// 返回函数的函数
function createMultiplier(factor: number): (value: number) => number {
    return function(value: number): number {
        return value * factor;
    };
}

let double = createMultiplier(2);
let triple = createMultiplier(3);

console.log(double(5));  // 10
console.log(triple(5));  // 15

联合类型和交叉类型

1. 联合类型 (Union Types)

// 基本联合类型
let value: string | number;
value = "hello";  // ✅ 正确
value = 42;       // ✅ 正确
// value = true;  // ❌ 错误

// 函数参数的联合类型
function formatValue(value: string | number): string {
    if (typeof value === "string") {
        return value.toUpperCase();
    } else {
        return value.toString();
    }
}

// 字面量联合类型
type Status = "loading" | "success" | "error";
let currentStatus: Status = "loading";

// 对象联合类型
interface Bird {
    type: "bird";
    flySpeed: number;
}

interface Fish {
    type: "fish";
    swimSpeed: number;
}

type Animal = Bird | Fish;

function moveAnimal(animal: Animal): void {
    switch (animal.type) {
        case "bird":
            console.log(`Flying at ${animal.flySpeed} km/h`);
            break;
        case "fish":
            console.log(`Swimming at ${animal.swimSpeed} km/h`);
            break;
    }
}

2. 交叉类型 (Intersection Types)

// 基本交叉类型
interface Person {
    name: string;
    age: number;
}

interface Employee {
    employeeId: number;
    department: string;
}

type PersonEmployee = Person & Employee;

let employee: PersonEmployee = {
    name: "张三",
    age: 30,
    employeeId: 12345,
    department: "IT"
};

// 函数类型的交叉
type Logger = {
    log: (message: string) => void;
};

type Timestamped = {
    timestamp: Date;
};

type TimestampedLogger = Logger & Timestamped;

let logger: TimestampedLogger = {
    log: (message: string) => console.log(message),
    timestamp: new Date()
};

3. 类型守卫 (Type Guards)

// typeof 类型守卫
function processValue(value: string | number): string {
    if (typeof value === "string") {
        // 在这个块中,TypeScript 知道 value 是 string
        return value.toUpperCase();
    } else {
        // 在这个块中,TypeScript 知道 value 是 number
        return value.toString();
    }
}

// instanceof 类型守卫
class Dog {
    bark(): void {
        console.log("Woof!");
    }
}

class Cat {
    meow(): void {
        console.log("Meow!");
    }
}

function makeSound(animal: Dog | Cat): void {
    if (animal instanceof Dog) {
        animal.bark(); // TypeScript 知道这是 Dog
    } else {
        animal.meow(); // TypeScript 知道这是 Cat
    }
}

// 自定义类型守卫
function isString(value: unknown): value is string {
    return typeof value === "string";
}

function processUnknown(value: unknown): void {
    if (isString(value)) {
        // TypeScript 知道 value 是 string
        console.log(value.toUpperCase());
    }
}

字面量类型

1. 字符串字面量类型

// 字符串字面量类型
type Direction = "north" | "south" | "east" | "west";

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

move("north");  // ✅ 正确
// move("up");  // ❌ 错误

// 模板字面量类型 (TypeScript 4.1+)
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
type HoverEvent = EventName<"hover">; // "onHover"

// 更复杂的模板字面量
type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
type ApiEndpoint<T extends string, M extends HttpMethod> = `${M} /api/${T}`;

type UserEndpoints = 
    | ApiEndpoint<"users", "GET">     // "GET /api/users"
    | ApiEndpoint<"users", "POST">    // "POST /api/users"
    | ApiEndpoint<"users", "PUT">     // "PUT /api/users"
    | ApiEndpoint<"users", "DELETE">; // "DELETE /api/users"

2. 数字字面量类型

// 数字字面量类型
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;

function rollDice(): DiceRoll {
    return (Math.floor(Math.random() * 6) + 1) as DiceRoll;
}

// 配置对象中的字面量类型
interface Config {
    version: 1 | 2 | 3;
    mode: "development" | "production" | "test";
    port: 3000 | 8080 | 9000;
}

let config: Config = {
    version: 2,
    mode: "development",
    port: 3000
};

3. 布尔字面量类型

// 布尔字面量类型
type Success = true;
type Failure = false;

interface ApiResult<T extends boolean> {
    success: T;
    data: T extends true ? any : null;
    error: T extends false ? string : null;
}

let successResult: ApiResult<true> = {
    success: true,
    data: { id: 1, name: "张三" },
    error: null
};

let failureResult: ApiResult<false> = {
    success: false,
    data: null,
    error: "网络错误"
};

类型断言

1. 基本类型断言

// 尖括号语法(在 JSX 中不可用)
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;

// as 语法(推荐)
let someValue2: unknown = "this is a string";
let strLength2: number = (someValue2 as string).length;

// DOM 元素的类型断言
let inputElement = document.getElementById("myInput") as HTMLInputElement;
inputElement.value = "Hello World";

2. 非空断言操作符

// 非空断言操作符 (!)
function processUser(user: { name: string; email?: string }): void {
    // 我们确定 email 存在
    let emailLength = user.email!.length;
    console.log(`Email length: ${emailLength}`);
}

// 数组元素的非空断言
let numbers = [1, 2, 3, 4, 5];
let lastNumber = numbers.pop()!; // 我们确定数组不为空

3. 双重断言

// 双重断言(谨慎使用)
let value: string = "hello";
let numberValue: number = (value as unknown) as number;
// 这样做是危险的,应该避免

4. const 断言

// const 断言
let colors = ["red", "green", "blue"] as const;
// colors 的类型是 readonly ["red", "green", "blue"]

let config = {
    apiUrl: "https://api.example.com",
    timeout: 5000
} as const;
// config 的类型是 { readonly apiUrl: "https://api.example.com"; readonly timeout: 5000; }

// 用于创建字面量类型
function handleStatus(status: "success" | "error" | "loading"): void {
    // 处理状态
}

let currentStatus = "success" as const;
handleStatus(currentStatus); // ✅ 正确,因为 currentStatus 的类型是 "success"

实践练习

练习1:用户管理系统

// 定义用户角色枚举
enum UserRole {
    Admin = "admin",
    User = "user",
    Guest = "guest"
}

// 定义用户状态
type UserStatus = "active" | "inactive" | "pending";

// 定义用户接口
interface User {
    readonly id: number;
    username: string;
    email: string;
    role: UserRole;
    status: UserStatus;
    profile?: {
        firstName: string;
        lastName: string;
        avatar?: string;
    };
    createdAt: Date;
    lastLoginAt?: Date;
}

// 创建用户函数
function createUser(
    username: string,
    email: string,
    role: UserRole = UserRole.User
): Omit<User, 'id' | 'createdAt'> {
    return {
        username,
        email,
        role,
        status: "pending"
    };
}

// 用户验证函数
function validateUser(user: User): boolean {
    return user.username.length > 0 && 
           user.email.includes('@') && 
           Object.values(UserRole).includes(user.role);
}

练习2:购物车系统

// 商品接口
interface Product {
    id: number;
    name: string;
    price: number;
    category: string;
    inStock: boolean;
}

// 购物车项目
interface CartItem {
    product: Product;
    quantity: number;
}

// 购物车类
class ShoppingCart {
    private items: CartItem[] = [];

    addItem(product: Product, quantity: number = 1): void {
        if (!product.inStock) {
            throw new Error("商品缺货");
        }

        const existingItem = this.items.find(item => item.product.id === product.id);
        
        if (existingItem) {
            existingItem.quantity += quantity;
        } else {
            this.items.push({ product, quantity });
        }
    }

    removeItem(productId: number): void {
        this.items = this.items.filter(item => item.product.id !== productId);
    }

    getTotal(): number {
        return this.items.reduce((total, item) => {
            return total + (item.product.price * item.quantity);
        }, 0);
    }

    getItems(): readonly CartItem[] {
        return this.items;
    }
}

练习3:API 响应处理

// API 响应基础接口
interface BaseApiResponse {
    status: number;
    message: string;
    timestamp: Date;
}

// 成功响应
interface SuccessResponse<T> extends BaseApiResponse {
    success: true;
    data: T;
}

// 错误响应
interface ErrorResponse extends BaseApiResponse {
    success: false;
    error: {
        code: string;
        details?: string;
    };
}

// 联合类型
type ApiResponse<T> = SuccessResponse<T> | ErrorResponse;

// API 调用函数
async function fetchUser(id: number): Promise<ApiResponse<User>> {
    try {
        // 模拟 API 调用
        const user: User = {
            id,
            username: "testuser",
            email: "test@example.com",
            role: UserRole.User,
            status: "active",
            createdAt: new Date()
        };

        return {
            success: true,
            status: 200,
            message: "用户获取成功",
            timestamp: new Date(),
            data: user
        };
    } catch (error) {
        return {
            success: false,
            status: 500,
            message: "服务器错误",
            timestamp: new Date(),
            error: {
                code: "INTERNAL_ERROR",
                details: error instanceof Error ? error.message : "未知错误"
            }
        };
    }
}

// 响应处理函数
function handleUserResponse(response: ApiResponse<User>): void {
    if (response.success) {
        console.log("用户信息:", response.data);
    } else {
        console.error("错误:", response.error.code, response.error.details);
    }
}

总结

TypeScript 的基础类型系统为我们提供了强大的类型安全保障。通过掌握这些基础概念:

  1. 原始类型:string, number, boolean, null, undefined, void, never
  2. 数组和元组:提供集合数据的类型安全
  3. 枚举:创建命名常量集合
  4. 对象类型:描述复杂数据结构
  5. 函数类型:确保函数签名的正确性
  6. 联合和交叉类型:组合类型以创建更复杂的类型
  7. 字面量类型:精确的值类型
  8. 类型断言:在必要时覆盖类型推断

这些基础知识将为学习更高级的 TypeScript 特性打下坚实的基础。在实际项目中,建议从简单的类型注解开始,逐步引入更复杂的类型定义,以提高代码的可维护性和可靠性。