TypeScript学习-第3章:复合类型

TypeScript学习-第3章:复合类型

各位前端工友们,上一章咱们搞定了基础类型,相当于摸清了TS世界里的"单个零件"------字符串、数字这些独立个体。但实际开发中,咱们面对的都是"组装件":比如用户列表是多个用户对象组成的集合,一条订单信息里既有字符串姓名,又有数字金额。

这一章咱们就解锁"零件组装技能"------复合类型,核心搞定数组、元组、对象这三大主力,学会给复杂数据结构"贴精准标签"。如果说基础类型是"单兵作战",那复合类型就是"团队协作",掌握它们才能应对真实业务场景。

一、数组类型:同类元素的"整齐队列"

数组咱们在JS里天天用,本质是"一组相同类型的数据集合"。TS给数组加类型约束,就是给这个队列定规矩:"所有人必须是同一种身份",避免混进"异类"。咱们先讲两种声明方式,再聊只读数组的特殊用法。

1. 两种声明方式:各有适配场景

TS里数组有两种标注风格,按需选择即可,核心效果一致:

  • 简洁式:type[] ------日常开发首选,写法清爽。在基础类型后面加[],就表示"该类型的数组"。
typescript 复制代码
    // 数字数组:只能存数字
    let scores: number[] = [90, 85, 95];
    // 字符串数组:只能存字符串
    let names: string[] = ["张三", "李四", "王五"];
    // 错误示例:混存不同类型会标红
    // let mixArr: number[] = [10, "20"]; // 类型不匹配
  • 泛型式:Array<type> ------基于泛型语法(后续章节会深入),适合复杂场景(如嵌套泛型)。写法是Array后跟尖括号,里面填元素类型。
typescript 复制代码
    // 数字数组,和number[]等价
    let scores: Array<number> = [90, 85, 95];
    // 字符串数组,和string[]等价
    let names: Array<string> = ["张三", "李四", "王五"];

小提醒:日常开发用type[]就够了,只有当数组元素是泛型类型(比如后续的Array<Array<number>>二维数组)时,泛型式会更易读。

2. 只读数组:不能修改的"固定队列"

有些场景下,数组创建后就不能增删改元素(比如接口返回的固定列表),这时候就需要"只读数组",相当于给队列上了"锁",禁止任何修改操作。有两种声明方式:

  • readonly type[] ------简洁式,在类型前加readonly

    typescript 复制代码
    // 只读字符串数组
    let readonlyNames: readonly string[] = ["张三", "李四"];
    // 以下操作都会报错:只读数组禁止修改
    // readonlyNames.push("赵六"); // 禁止新增
    // readonlyNames[0] = "张小三"; // 禁止修改元素
    // readonlyNames.pop(); // 禁止删除
  • ReadonlyArray<type>------泛型式,和上面效果完全一致。

    typescript 复制代码
    let readonlyScores: ReadonlyArray<number> = [90, 85];
    // 同样禁止所有修改操作
    // readonlyScores[1] = 90; // 报错

避坑点:只读数组只是"禁止修改元素和长度",如果元素是对象(引用类型),对象内部的属性仍可修改(后续对象部分会细说)。比如readonly {name: string}[],数组不能增删,但里面对象的name可以改。

二、元组(Tuple):固定"人数"和"身份"的队列

数组是"同类元素的可变长队列",而元组是"固定长度、固定类型顺序的严格队列"------相当于规定了"队列里必须有n个人,第1个是A身份,第2个是B身份,不能多也不能少"。这种特性特别适合处理"结构固定的短数据"。

1. 元组的基础用法

声明语法:用[类型1, 类型2, ...]列出每个位置的类型,赋值时必须严格对应------长度一致、类型顺序一致。

复制代码
// 元组:第1个元素是字符串(姓名),第2个是数字(年龄),第3个是布尔(是否成年)
let person: [string, number, boolean] = ["张三", 25, true];

// 正确访问:按索引取对应类型的值
let name: string = person[0];
let age: number = person[1];

// 错误示例:类型不匹配或长度不一致
// let wrongPerson1: [string, number, boolean] = ["李四", "26", true]; // 第2个类型错误
// let wrongPerson2: [string, number, boolean] = ["李四", 26]; // 长度不足

2. 元组的避坑点与实用场景

元组看似简单,但有两个容易踩的坑,还有几个高频实用场景,咱们逐一梳理:

  1. 越界赋值的隐患 :虽然元组长度固定,但在非严格模式下,用push方法新增元素不会报错(严格模式下会警告),但新增元素的类型会被约束为元组所有类型的联合类型。

    typescript 复制代码
    let person: [string, number] = ["张三", 25];
    person.push(30); // 非严格模式下不报错,新增元素必须是string或number
    // person.push(true); // 报错:布尔值不属于元组类型的联合类型`建议:用元组就尽量保持"固定长度",避免手动增删元素,开启严格模式可减少此类隐患。
  2. 可选元素(TS 4.0+支持) :元组也可以有可选元素,用?标记,可选元素必须放在最后。

    复制代码
    // 第3个元素(手机号)是可选的
    let user: [string, number, string?] = ["张三", 25];
    user = ["李四", 26, "13800138000"]; // 也可以补全可选元素
  3. 实用场景:元组适合处理"结构固定的短数据",比如接口返回的坐标([x, y])、用户的账号密码组合、函数返回的多值结果等。

    复制代码
    // 场景1:坐标(x是数字,y是数字)
    let position: [number, number] = [100, 200];
    // 场景2:函数返回多值(成功状态+数据)
    function getUser(): [boolean, {name: string, age: number}] {
      return [true, {name: "张三", age: 25}];
    }

三、对象类型:多类型元素的"组合体"

对象是TS里最常用的复合类型,本质是"键值对的集合",每个键对应一个值,值可以是任意类型(基础类型、复合类型都可)。TS给对象加类型约束,就是给每个"键"指定对应的"值类型",避免乱赋值。

1. 直接声明:快速约束简单对象

对于结构简单的对象,可直接在变量后标注类型,格式为{ 键1: 类型1; 键2: 类型2; ... },多个键值对用分号分隔(逗号也可,习惯用分号更规范)。

typescript 复制代码
// 声明一个用户对象类型:name是字符串,age是数字,isAdult是布尔
let user: {
  name: string;
  age: number;
  isAdult: boolean;
} = {
  name: "张三",
  age: 25,
  isAdult: true
};

2. 可选属性:允许"可有可无"的键

实际开发中,有些对象属性不是必填的(比如用户的手机号、邮箱),这时候用?标记为可选属性------赋值时可以省略该属性,也可以补全。

typescript 复制代码
// 手机号(phone)是可选属性
let user: {
  name: string;
  age: number;
  phone?: string; // 可选属性
} = {
  name: "张三",
  age: 25
  // 省略phone属性,不报错
};

// 也可以补全可选属性
user.phone = "13800138000";

避坑点:访问可选属性时,TS会提示"可能为undefined",需要先判断是否存在再使用,避免运行时错误。

typescript 复制代码
// 正确写法:先判断phone是否存在
if (user.phone) {
  console.log(user.phone.length); // 不报错
}

3. 只读属性:初始化后不能修改的键

有些对象属性初始化后就不能修改(比如用户的ID、订单编号),用readonly标记为只读属性------只能在创建对象时赋值,后续无法修改。

typescript 复制代码
let user: {
  readonly id: number; // 只读属性
  name: string;
} = {
  id: 1001,
  name: "张三"
};

user.name = "张小三"; // 可修改
// user.id = 1002; // 报错:只读属性不能修改

和只读数组类似:只读属性只是"禁止修改属性值",如果属性值是对象(引用类型),对象内部的属性仍可修改。

四、实践:复合类型的嵌套玩法

真实业务场景中,复合类型很少单独使用,大多是"嵌套组合"------比如对象数组、元组嵌套对象、对象里包含数组等。咱们结合两个高频场景,实战演练如何标注类型。

场景1:对象数组(最常用)

比如接口返回的用户列表,是"多个用户对象组成的数组",标注时要先定义单个用户的对象类型,再用数组类型包裹。

typescript 复制代码
// 单个用户的对象类型
type User = {
  id: number;
  name: string;
  age: number;
  phone?: string;
};

// 用户列表:User类型的数组
let userList: User[] = [
  { id: 1001, name: "张三", age: 25 },
  { id: 1002, name: "李四", age: 26, phone: "13800138000" },
  { id: 1003, name: "王五", age: 24 }
];

// 访问嵌套属性:类型安全有提示
console.log(userList[0].name); // 张三(TS自动提示name属性)

场景2:元组嵌套对象

比如处理"用户信息+订单列表"的组合数据,元组第一个元素是用户对象,第二个元素是订单对象数组。

typescript 复制代码
// 订单对象类型
type Order = {
  orderId: string;
  amount: number;
};

// 元组:第1个元素是User对象,第2个是Order数组
let userWithOrders: [User, Order[]] = [
  { id: 1001, name: "张三", age: 25 },
  [
    { orderId: "OD2024001", amount: 99 },
    { orderId: "OD2024002", amount: 199 }
  ]
];

// 访问嵌套数据
console.log(userWithOrders[0].name); // 张三
console.log(userWithOrders[1][0].amount); // 99

场景3:对象嵌套数组+可选属性

比如商品信息,包含基础属性和可选的规格数组。

typescript 复制代码
type Product = {
  id: number;
  name: string;
  price: number;
  specs?: string[]; // 可选的规格数组
};

let product: Product = {
  id: 2001,
  name: "T恤",
  price: 99,
  specs: ["M码", "L码", "XL码"]
};

// 可选数组的安全访问
if (product.specs) {
  console.log(product.specs.length); // 3
}

五、本章小结:复合类型的核心是"结构化约束"

这一章咱们吃透了数组、元组、对象三大复合类型,核心逻辑可以总结为:复合类型是基础类型的"结构化组合",类型标注的本质是给"组合规则"贴标签------数组定"同类元素的队列规则",元组定"固定长度和顺序的队列规则",对象定"键值对的对应规则"。

新手容易踩的坑:一是混淆数组和元组的使用场景(变长用数组,定长用元组);二是忽略可选属性的undefined判断;三是误以为只读复合类型"内部属性也不可修改"。这些都需要通过实践慢慢磨合。

下一章咱们要升级技能,学习函数类型的精准约束------给函数的参数、返回值贴标签,让函数调用更安全、重构更放心。记得多动手写嵌套场景的代码,培养"结构化类型思维",咱们下一章见!

相关推荐
lxl13077 小时前
学习C++(5)运算符重载+赋值运算符重载
学习
摘星编程8 小时前
React Native + OpenHarmony:自定义useFormik表单处理
javascript·react native·react.js
pas1368 小时前
39-mini-vue 实现解析 text 功能
前端·javascript·vue.js
AutumnorLiuu8 小时前
C++并发编程学习(一)——线程基础
开发语言·c++·学习
CS创新实验室8 小时前
关于 Moltbot 的学习总结笔记
笔记·学习·clawdbot·molbot
峥嵘life8 小时前
Android EDLA CTS、GTS等各项测试命令汇总
android·学习·elasticsearch
千谦阙听8 小时前
数据结构入门:栈与队列
数据结构·学习·visual studio
.小墨迹9 小时前
C++学习——C++中`memcpy`和**赋值拷贝**的核心区别
java·linux·开发语言·c++·学习·算法·机器学习
2601_949833399 小时前
flutter_for_openharmony口腔护理app实战+我的实现
开发语言·javascript·flutter
望忆9 小时前
关于《Cold & Warm Net: Addressing Cold-Start Usersin Recommender Systems》
学习