我们之前学习了基础类型: number/string/boolean, 以及数组类型: 比如 number[], string[][]
但是当我们需要表示更复杂的数据时,比如游戏角色:
如果只用基础类型,我们需要:
这样做的问题:
数据是分散的,没有关联
难以创建多个角色
问题:
容易出错,比如忘记某个属性
无法进行批量操作
想象一下,如果要创建100个角色怎么办?
TypeScript 提供了完美的解决方案:对象!
对象是用来表示复杂数据结构的方式。它把相关的数据组合在一起。
对象语法结构:
{} 创建对象属性名: 属性值 的键值对name、level、score、isVictory"name"、"level"、"score"、"isVictory""勇者"15、1000true、false, 分隔(类似数组的元素分隔[1,2,3]).要访问对象中的数据,我们使用点号 .:
访问属性的语法:对象名.属性名
对象名.属性名 = 新值要修改对象中的数据,同样使用点号 .:
修改属性的语法:对象名.属性名 = 新值
= 设置新的值对象不仅可以使用固定的值,还可以使用变量来动态构造:
与变量赋值的核心原则关联:
这其实遵循了我们在《变量赋值和操作》中学到的核心原则:
name: playerName 中的 playerName 不在赋值符号 = 左边,所以是在读取 playerName 的值name 属性,没有额外的计算level: player.level + 1 中,先读取 player.level 的值(15),然后计算 15 + 1 = 16,最后将结果赋值给 level 属性关键理解:
let y = x + 1 的逻辑完全相同,只是赋值目标从变量变成了对象属性= 右边的所有操作,然后将结果赋值给左边的属性现在让我们看看 TypeScript 的神奇之处!
当我们创建变量时,TypeScript 会自动查看我们赋给变量的值,然后推断出它的类型:
TypeScript 同样会自动推断对象的类型:
TypeScript 推断的 player 类型是什么?
TypeScript 分析这个对象后,推断出 player 的类型为:
这个类型叫做"对象类型"(Object Type),它描述了一个对象应该有什么样的属性和属性类型。
大括号 {} - 对象类型标记
{} 是完全相同的符号属性定义 name: string;
name - 属性名称: - 分隔符(冒号),左边是属性名,右边是类型string - 属性类型,表示这个属性必须是字符串, - 结尾符,分隔不同的属性定义多个属性定义
属性名: 类型; 的格式, 分隔其实这个自动推断出来的类型,我们完全可以自己手写出来!
这和我们之前学的类型一样:
数组类型自动推断:let scores = [85, 92, 78] → TypeScript 推断为 number[]
数组类型手动指定:let scores: number[] = [85, 92, 78]
对象类型自动推断:let player = { name: "勇者", level: 15, health: 100 } → TypeScript 推断为 { name: string, level: number, health: number }
对象类型手动指定:let player: { name: string, level: number, health: number } = { name: "勇者", level: 15, health: 100 }
重点:这个推断出的对象类型和 number、string、number[] 等类型完全一样!
我们可以通过下面的例子来理解:
仔细看这个对比:
语法完全相同:
let 变量名: 类型 = 值; 的格式完全一样: 的作用完全一样:指定类型= 的作用完全一样:赋值类型的本质相同:
number 是数字的类型string 是字符串的类型number[] 是数字数组的类型{ name: string; level: number; health: number } 是特定结构对象的类型无论是自动推断还是手动指定,TypeScript 都会进行类型检查:
关键理解:
就像我们在《变量和赋值》课程中学到的基础类型一样,对象类型也遵循这个重要原则:
核心理解:
为什么这个原则很重要?
这是 TypeScript 一个非常重要的特性,主要体现在变量赋值的类型检查中。
在 TypeScript 中,当我们进行变量赋值时,总会做类型检查:
string 类型的值不能赋值给 number 类型的变量让我们通过例子来理解是如何做结构检查的:
为什么这是正确的?
player1 自动推断出的类型是:{name: string, level: number, health: number}player2 自动推断出的类型是:{name: string, level: number, health: number}为什么这是正确的?
player1 自动推断出的类型是:{name: string, level: number, health: number}player2 自动推断出的类型是:{level: number, health: number, name: string}关键理解: TypeScript 的类型结构检查只关心属性名和类型是否匹配,不关心属性的顺序。
player1 自动推断出的类型是:{name: string, level: number, health: number}player2 自动推断出的类型是:{name: string, level: number}(缺少 health 属性)为什么 player1 = player2 会报错?
player1 需要 health 属性,但 player2 没有,所以不能赋值。
错误信息分析:
错误信息解读:
'health' 属性缺失:源对象缺少必要的 health 属性in type '{ name: string; level: number; }':这是player2对象的类型结构but required in type '{ name: string; level: number; health: number; }':这是player1对象的类型结构,说明 health 属性是必须的为什么 player2 = player1 是正确的?
player1 有 name 和 level 属性,满足了 player2 的所有要求。虽然 player1 多了 health 属性,但这是可以接受的,就像一个人有额外的技能不会影响他完成基础工作一样。
关键理解:TypeScript 的结构化类型系统规则
=右边的)可以比目标类型有更多属性,但不能缺少必需属性, 如果源对象具备了目标对象要求的所有属性,那么它就是兼容的,即使有额外的属性也无妨TypeScript 结构化类型系统的检查规则:
name ≠ namstring = string,number = number通过这节课,我们学习了:
重要记住:
{} 和 属性名: 类型; 定义. 访问和修改属性对象类型是 TypeScript 的基础特性,理解它将为学习对象数组打下坚实基础!