← 编程学习中心
📖

Preparation

8 个课程
1
如何学习编程
2
计算机基础
3
键盘初识与基准键位
4
字母与数字基础
5
Shift键魔法
6
编程符号专精
7
功能键与修饰键
8
键盘学习总结与进阶
📚

Basics

22 个课程
1
什么是编程?
2
Hello World - 你的第一个程序
3
变量基础概念 - 给信息贴标签
4
变量赋值和操作 - 变量的高级用法
5
数据类型基础 - 数字和字符串
6
基础运算符 - 数字运算王国
7
布尔类型与比较运算符 - 程序的判断基础
8
条件判断基础 - 让程序学会做决定
9
变量作用域 - 变量的生存范围
10
条件语句进阶 - 复杂判断与嵌套结构
11
逻辑运算符 - 简化复杂条件判断
12
循环语句 - 程序的重复机器
13
循环进阶 - break、continue与循环控制
14
嵌套循环与作用域
15
循环调试实战技巧
16
循环不变量 - 理解循环的数学规律
17
数组基础 - 数据的收纳盒
18
数组操作方法 - 添加、删除和管理
19
数组高级 - 探索数组的强大功能
20
引用概念 - 变量的不同行为
21
函数基础 - 解决重复代码的秘密武器
22
函数实践与应用 - 巩固基础,实战应用
🚀

Intermediate

11 个课程
1
从控制台到画布 - p5.js初体验
2
图形绘制工具箱 - 基础图形函数
3
让世界多彩 - 颜色系统详解
4
文字的艺术 - 文本绘制与样式
5
球体动起来 - 动画基础入门
6
球体的交互魔法 - 鼠标与条件判断
7
面向对象编程 - Class类与实例
8
球的家族 - 循环与数组应用
9
球的个性 - 函数封装与参数化
10
球的变身 - 形状模式与高级绘制
11
球的世界 - 综合创作项目
🎯

Advanced

6 个课程
1
对象类型
2
对象类型应用
3
自定义类型
4
函数进阶 - 箭头函数与高级编程技巧
5
内置方法 - 字符串与数组的魔法工具
6
Switch语句与字符串模板 - 更优雅的代码表达
← 编程学习中心
📖

Preparation

8 个课程
1
如何学习编程
2
计算机基础
3
键盘初识与基准键位
4
字母与数字基础
5
Shift键魔法
6
编程符号专精
7
功能键与修饰键
8
键盘学习总结与进阶
📚

Basics

22 个课程
1
什么是编程?
2
Hello World - 你的第一个程序
3
变量基础概念 - 给信息贴标签
4
变量赋值和操作 - 变量的高级用法
5
数据类型基础 - 数字和字符串
6
基础运算符 - 数字运算王国
7
布尔类型与比较运算符 - 程序的判断基础
8
条件判断基础 - 让程序学会做决定
9
变量作用域 - 变量的生存范围
10
条件语句进阶 - 复杂判断与嵌套结构
11
逻辑运算符 - 简化复杂条件判断
12
循环语句 - 程序的重复机器
13
循环进阶 - break、continue与循环控制
14
嵌套循环与作用域
15
循环调试实战技巧
16
循环不变量 - 理解循环的数学规律
17
数组基础 - 数据的收纳盒
18
数组操作方法 - 添加、删除和管理
19
数组高级 - 探索数组的强大功能
20
引用概念 - 变量的不同行为
21
函数基础 - 解决重复代码的秘密武器
22
函数实践与应用 - 巩固基础,实战应用
🚀

Intermediate

11 个课程
1
从控制台到画布 - p5.js初体验
2
图形绘制工具箱 - 基础图形函数
3
让世界多彩 - 颜色系统详解
4
文字的艺术 - 文本绘制与样式
5
球体动起来 - 动画基础入门
6
球体的交互魔法 - 鼠标与条件判断
7
面向对象编程 - Class类与实例
8
球的家族 - 循环与数组应用
9
球的个性 - 函数封装与参数化
10
球的变身 - 形状模式与高级绘制
11
球的世界 - 综合创作项目
🎯

Advanced

6 个课程
1
对象类型
2
对象类型应用
3
自定义类型
4
函数进阶 - 箭头函数与高级编程技巧
5
内置方法 - 字符串与数组的魔法工具
6
Switch语句与字符串模板 - 更优雅的代码表达

对象类型

为什么要学习对象类型?

我们之前学习了基础类型: number/string/boolean, 以及数组类型: 比如 number[], string[][]

但是当我们需要表示更复杂的数据时,比如游戏角色:

  • 角色有名字
  • 角色有等级
  • 角色有血量

如果只用基础类型,我们需要:

加载代码编辑器...

这样做的问题:

  1. 数据是分散的,没有关联

    • 角色的信息被拆分成多个独立的变量
    • 无法看出这些变量属于同一个角色
  2. 难以创建多个角色

    加载代码编辑器...

    问题:

    • 每次都要创建4个变量,很麻烦
    • 变量名容易搞混(name1 vs name2)
    • 无法用数组或循环来批量处理
  3. 容易出错,比如忘记某个属性

    加载代码编辑器...
  4. 无法进行批量操作

    加载代码编辑器...

想象一下,如果要创建100个角色怎么办?

  • 需要创建400个变量!
  • 变量名会非常复杂(name1, name2, ..., name100)
  • 根本无法管理

TypeScript 提供了完美的解决方案:对象!

对象语法基础

什么是对象?

对象是用来表示复杂数据结构的方式。它把相关的数据组合在一起。

加载代码编辑器...

对象语法结构:

  • 使用大括号 {} 创建对象
  • 内部是 属性名: 属性值 的键值对
  • 属性名:可以加引号也可以不加,但我们推荐不加引号
    • 不加引号(推荐):name、level、score、isVictory
    • 加引号(也可以):"name"、"level"、"score"、"isVictory"
    • 属性名就像是嵌套在对象内部的变量名
加载代码编辑器...
  • 属性值:可以是任何有效的数据类型
    • 字符串值需要加引号:"勇者"
    • 数字值直接写:15、1000
    • 布尔值直接写:true、false
  • 多个属性用逗号 , 分隔(类似数组的元素分隔[1,2,3])

属性访问语法:点号 .

要访问对象中的数据,我们使用点号 .:

访问属性的语法:对象名.属性名

  • 点号左边是对象变量名
  • 点号右边是属性名
  • 这样就可以获取到属性的具体值
加载代码编辑器...

属性修改语法:对象名.属性名 = 新值

要修改对象中的数据,同样使用点号 .:

修改属性的语法:对象名.属性名 = 新值

  • 点号指定要修改的属性
  • 赋值符号 = 设置新的值
加载代码编辑器...

动态构造对象

对象不仅可以使用固定的值,还可以使用变量来动态构造:

加载代码编辑器...

与变量赋值的核心原则关联:

这其实遵循了我们在《变量赋值和操作》中学到的核心原则:

  1. 读取操作:name: playerName 中的 playerName 不在赋值符号 = 左边,所以是在读取 playerName 的值
  2. 简单赋值:将读取到的值直接赋值给对象的 name 属性,没有额外的计算
  3. 读取+计算+赋值:level: player.level + 1 中,先读取 player.level 的值(15),然后计算 15 + 1 = 16,最后将结果赋值给 level 属性

关键理解:

  • 对象构造时的属性赋值,本质上就是我们熟悉的 "读取→计算(可选)→赋值" 过程
  • 这与 let y = x + 1 的逻辑完全相同,只是赋值目标从变量变成了对象属性
  • TypeScript 会先执行 = 右边的所有操作,然后将结果赋值给左边的属性

TypeScript 的类型推断与对象类型详解

现在让我们看看 TypeScript 的神奇之处!

什么是类型推断?

当我们创建变量时,TypeScript 会自动查看我们赋给变量的值,然后推断出它的类型:

加载代码编辑器...

对象的类型推断

TypeScript 同样会自动推断对象的类型:

加载代码编辑器...

TypeScript 推断的 player 类型是什么? TypeScript 分析这个对象后,推断出 player 的类型为:

{
  name: string,
  level: number
  health: number
}

这个类型叫做"对象类型"(Object Type),它描述了一个对象应该有什么样的属性和属性类型。

  1. 大括号 {} - 对象类型标记

    • 这和我们创建对象时使用的 {} 是完全相同的符号
    • 表示这是一个对象类型的结构定义
  2. 属性定义 name: string;

    • name - 属性名称
    • : - 分隔符(冒号),左边是属性名,右边是类型
    • string - 属性类型,表示这个属性必须是字符串
    • , - 结尾符,分隔不同的属性定义
  3. 多个属性定义

    • 每个属性都是 属性名: 类型; 的格式
    • 多个属性之间用分号 , 分隔

手动指定对象类型

其实这个自动推断出来的类型,我们完全可以自己手写出来!

这和我们之前学的类型一样:

  • 数组类型自动推断: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[] 等类型完全一样!

我们可以通过下面的例子来理解:

加载代码编辑器...

仔细看这个对比:

  1. 语法完全相同:

    • let 变量名: 类型 = 值; 的格式完全一样
    • 冒号 : 的作用完全一样:指定类型
    • 赋值符号 = 的作用完全一样:赋值
  2. 类型的本质相同:

    • number 是数字的类型
    • string 是字符串的类型
    • number[] 是数字数组的类型
    • { name: string; level: number; health: number } 是特定结构对象的类型
    • 它们都是 TypeScript 的类型,地位完全相同!, 都可以用来声明变量类型, 指定函数输入/输出参数类型等等

类型检查依然有效!

无论是自动推断还是手动指定,TypeScript 都会进行类型检查:

加载代码编辑器...

关键理解:

  • 自动推断:TypeScript 根据初始值推断类型,然后进行类型检查
  • 手动指定:我们明确告诉 TypeScript 类型,然后进行类型检查
  • 检查效果相同:无论是哪种方式,TypeScript 都会进行严格的类型检查
  • 错误提示相同:类型错误时,TypeScript 会给出相同的错误信息

重要原则:变量类型一旦确定就不能修改

就像我们在《变量和赋值》课程中学到的基础类型一样,对象类型也遵循这个重要原则:

加载代码编辑器...

核心理解:

  1. 类型锁定:一旦 TypeScript 确定了变量的类型,这个类型就不能改变
  2. 属性值可变:可以修改对象内部属性的值,但不能改变属性的类型
  3. 结构固定:对象的结构(有哪些属性、什么类型)是固定的
  4. 统一原则:这个规则适用于所有类型,包括基础类型、数组类型和对象类型

为什么这个原则很重要?

  • 类型安全:确保程序中每个变量都有明确的类型
  • 代码可读性:能清楚地知道每个变量的类型结构

TypeScript 的结构化类型系统

这是 TypeScript 一个非常重要的特性,主要体现在变量赋值的类型检查中。

在 TypeScript 中,当我们进行变量赋值时,总会做类型检查:

  • 基础类型赋值:string 类型的值不能赋值给 number 类型的变量
  • 对象类型赋值:TypeScript 会检查右边对象值的类型结构是否符合左边变量的类型要求

让我们通过例子来理解是如何做结构检查的:

1. 结构匹配的双向赋值

加载代码编辑器...

为什么这是正确的?

  • player1 自动推断出的类型是:{name: string, level: number, health: number}
  • player2 自动推断出的类型是:{name: string, level: number, health: number}
  • 两个类型结构完全相同,所以它们是兼容的

2. 字段顺序不影响兼容性

加载代码编辑器...

为什么这是正确的?

  • player1 自动推断出的类型是:{name: string, level: number, health: number}
  • player2 自动推断出的类型是:{level: number, health: number, name: string}
  • 两个类型结构完全相同(只是属性顺序不同),所以它们是兼容的

关键理解: TypeScript 的类型结构检查只关心属性名和类型是否匹配,不关心属性的顺序。

3. 属性缺失的类型兼容性

加载代码编辑器...
  • 类型分析:
  • player1 自动推断出的类型是:{name: string, level: number, health: number}
  • player2 自动推断出的类型是:{name: string, level: number}(缺少 health 属性)

为什么 player1 = player2 会报错?

player1 需要 health 属性,但 player2 没有,所以不能赋值。

错误信息分析:

Property 'health' is missing in type '{ name: string; level: number; }'
but required in type '{ name: string; level: number; health: number; }'.

错误信息解读:

  • '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 结构化类型系统的检查规则:

  1. 属性名必须完全匹配:name ≠ nam
  2. 属性类型必须匹配:string = string,number = number
  3. 可以多,不能少:源类型可以比目标类型有更多属性,但不能缺少必需属性
  4. 顺序不重要:属性的排列顺序不影响兼容性

总结

通过这节课,我们学习了:

  1. 对象语法:创建、访问和修改对象属性
  2. 类型推断:TypeScript 自动分析对象结构
  3. 结构化类型:基于结构兼容性的类型系统
  4. 对象类型定义:手动指定对象类型的语法
  5. 类型检查:TypeScript 帮助我们发现和防止错误

重要记住:

  • 对象类型:用 {} 和 属性名: 类型; 定义
  • 类型推断:TypeScript 会自动推断对象类型
  • 结构兼容性:TypeScript 关注结构,不关注变量名称
  • 属性访问:使用点号 . 访问和修改属性
  • 类型安全:一旦类型确定就不能改变,但属性值可以修改

对象类型是 TypeScript 的基础特性,理解它将为学习对象数组打下坚实基础!

Next
对象类型应用
Next lesson