Day 03 - 条件控制
目标:掌握ArkTS的条件语句、分支控制和类型守卫模式
预计时间:2-3小时
第一部分:if 语句
1.1 单分支 if
语法与C++相同:当条件为 true 时执行代码块。
// IoT场景:温度超限报警
let temperature: number = 85.5;
if (temperature > 80.0) {
console.log(`温度警告:当前${temperature}°C,超过阈值80°C`);
}
⚠️ ArkTS要求:if 后的条件表达式必须是 boolean 类型或可隐式转换为 boolean 的类型。
1.2 双分支 if-else
// IoT场景:设备在线状态判断
let isOnline: boolean = false;
if (isOnline) {
console.log("设备在线,可以发送控制指令");
} else {
console.log("设备离线,无法通信");
}
// 实际应用:设置默认值
let inputName: string = "";
let deviceName: string;
if (inputName !== "") {
deviceName = inputName;
} else {
deviceName = "未命名设备";
}
console.log(deviceName); // "未命名设备"
1.3 多分支 if-else if-else
// IoT场景:温度分级报警系统
let temperature: number = 75.0;
if (temperature >= 90.0) {
console.log("严重过热,立即关闭设备!");
} else if (temperature >= 80.0) {
console.log("温度过高,启动紧急散热");
} else if (temperature >= 60.0) {
console.log("温度偏高,注意监控");
} else if (temperature >= 20.0) {
console.log("温度正常");
} else {
console.log("温度过低,检查环境");
}
执行逻辑:
- 从上到下依次判断条件
- 遇到第一个为
true的条件,执行对应代码块,然后跳出整个 if 结构 - 如果所有条件都不满足,执行 else 块(如果有)
1.4 嵌套 if 与优化技巧
嵌套 if 示例:
// IoT场景:设备综合状态判断
let isOnline: boolean = true;
let hasError: boolean = false;
let voltage: number = 220.0;
if (isOnline) {
if (hasError) {
console.log("设备在线但有故障");
} else {
if (voltage >= 200.0 && voltage <= 240.0) {
console.log("设备运行正常");
} else {
console.log("设备在线但电压异常");
}
}
} else {
console.log("设备离线");
}
优化技巧1:提前返回(减少嵌套)
// 优化后:使用提前返回
function checkDeviceStatus(
isOnline: boolean,
hasError: boolean,
voltage: number
): string {
if (!isOnline) {
return "设备离线";
}
if (hasError) {
return "设备在线但有故障";
}
if (voltage < 200.0 || voltage > 240.0) {
return "设备在线但电压异常";
}
return "设备运行正常";
}
console.log(checkDeviceStatus(true, false, 220.0)); // "设备运行正常"
优化技巧2:反转条件
// 反转条件减少嵌套
function processSensorData(data: number[] | null): void {
// ❌ 不好的写法:嵌套过深
// if (data !== null) {
// if (data.length > 0) {
// // 处理数据
// }
// }
// ✅ 优化:先处理边界情况
if (data === null) {
console.log("数据为空");
return;
}
if (data.length === 0) {
console.log("数据长度为0");
return;
}
// 主逻辑在这里,无嵌套
console.log(`处理${data.length}条数据`);
}
1.5 条件表达式的隐式转换规则(truthy/falsy)
ArkTS 支持数字、字符串等类型的隐式转换为 boolean!
| 类型 | falsy 值(转换为 false) | truthy 值(转换为 true) |
|---|---|---|
number |
0 |
非零数字 |
string |
""(空字符串) |
非空字符串 |
boolean |
false |
true |
null |
null |
— |
undefined |
undefined |
— |
// number 的隐式转换
let count: number = 5;
if (count) {
console.log("数量非零"); // 会执行
}
let zero: number = 0;
if (zero) {
console.log("这不会执行"); // 0 是 falsy
} else {
console.log("数量为零"); // 会执行
}
// string 的隐式转换
let deviceId: string = "SENSOR_001";
if (deviceId) {
console.log("设备ID有效"); // 会执行
}
let emptyName: string = "";
if (emptyName) {
console.log("这不会执行"); // 空字符串是 falsy
} else {
console.log("名称为空"); // 会执行
}
// null/undefined 的隐式转换
let sensor: string | null = null;
if (sensor) {
console.log("传感器已连接");
} else {
console.log("传感器未连接"); // 会执行(null 是 falsy)
}
⚠️ 最佳实践:虽然支持隐式转换,但在复杂场景下建议显式比较,提高可读性:
// 简单场景:隐式转换简洁
if (count) { }
// 复杂场景:显式比较更清晰
if (temperature > 80.0 && isOnline && !hasError) { }
【第一部分小结】
| 语法 | 说明 | 示例 |
|---|---|---|
if |
单分支 | if (x > 0) { } |
if-else |
双分支 | if (x > 0) { } else { } |
if-else if-else |
多分支 | if (x > 0) { } else if (x < 0) { } else { } |
| 提前返回 | 减少嵌套 | if (!valid) return; |
| 条件转换 | 支持隐式转换 | if (count), if (name) |
第二部分:switch 语句
2.1 基本语法
语法与C++相同:根据表达式的值跳转到匹配的 case。
// IoT场景:设备类型处理
let deviceType: string = "LED";
let brightness: number = 0;
switch (deviceType) {
case "LED":
console.log("LED灯设备");
brightness = 100;
break;
case "RGB":
console.log("RGB彩灯设备");
brightness = 100;
break;
case "DIMM":
console.log("可调光灯设备");
brightness = 50;
break;
default:
console.log("未知设备类型");
brightness = 0;
}
console.log(`亮度设置为: ${brightness}`);
2.2 fall-through(穿透)
故意省略 break,让多个 case 共享代码:
// IoT场景:HTTP状态码分类
let statusCode: number = 404;
switch (statusCode) {
case 200:
case 201:
case 204:
console.log("请求成功");
break;
case 400:
case 401:
case 403:
case 404:
console.log("客户端错误");
break;
case 500:
case 502:
case 503:
console.log("服务器错误");
break;
default:
console.log("未知状态码");
}
2.3 enum + switch 结合(ArkTS 最佳实践)
结合 Day 01 学过的枚举,实现类型安全的状态机:
// IoT场景:设备状态枚举
enum DeviceState {
OFFLINE = 0,
CONNECTING = 1,
ONLINE = 2,
ERROR = 3
}
function handleDeviceState(state: DeviceState): string {
switch (state) {
case DeviceState.OFFLINE:
return "设备离线,尝试重连";
case DeviceState.CONNECTING:
return "设备连接中,请稍候";
case DeviceState.ONLINE:
return "设备在线,正常工作";
case DeviceState.ERROR:
return "设备故障,需要维修";
default:
// ArkTS 编译器会检查是否覆盖所有枚举值
// 这里的 default 用于防御性编程
return `未知状态: ${state}`;
}
}
let currentState: DeviceState = DeviceState.ONLINE;
console.log(handleDeviceState(currentState)); // "设备在线,正常工作"
字符串枚举同样适用:
enum CommandType {
POWER_ON = "POWER_ON",
POWER_OFF = "POWER_OFF",
SET_BRIGHTNESS = "SET_BRIGHTNESS",
GET_STATUS = "GET_STATUS"
}
function executeCommand(cmd: CommandType, param?: string): string {
switch (cmd) {
case CommandType.POWER_ON:
return "设备已开启";
case CommandType.POWER_OFF:
return "设备已关闭";
case CommandType.SET_BRIGHTNESS:
return `亮度设置为${param ?? "50"}%`;
case CommandType.GET_STATUS:
return "设备状态正常";
default:
return "未知命令";
}
}
2.4 switch 的本质与类型支持
switch 的本质:case 匹配使用的是 === 严格相等比较。
switch (x) {
case a: // 相当于 if (x === a)
break;
}
支持的类型:
number、string:可以直接使用- 联合类型
number | string:case 可以分别匹配不同类型的值 - 引用类型(数组、对象):比较的是地址,不是内容(很少使用)
类型检查:case 值的类型必须与 switch 表达式类型有交集,否则编译错误。
let x: string = "A";
let y: number = 1;
switch (x) {
case y: // ❌ 编译错误:string 和 number 无交集
break;
}
2.5 switch vs if 选择指南
| 场景 | 推荐 | 原因 |
|---|---|---|
| 单个变量与多个确定值比较 | switch | 结构清晰,编译器可优化 |
| 范围判断(如 > 80) | if | switch 无法处理范围 |
| 复杂条件组合 | if | switch 只能做相等比较 |
| 超过5个分支的相等比较 | switch | 可读性更好 |
| 枚举值处理 | switch | 类型安全,编译器检查完整性 |
// switch 适合:状态码、命令类型、枚举值
switch (command) {
case CommandType.POWER_ON:
console.log("开启");
break;
case CommandType.POWER_OFF:
console.log("关闭");
break;
}
// if 适合:范围判断、复杂条件
if (temperature > 80.0 && isOnline) { }
else if (voltage < 200.0 || voltage > 240.0) { }
【第二部分小结】
| 特性 | 语法 | 说明 |
|---|---|---|
| 基本语法 | switch (expr) { case val: ... break; } |
与C++相同 |
| fall-through | 省略 break | 多个case共享代码 |
| enum + switch | case Enum.VALUE: |
ArkTS最佳实践 |
| default | default: |
处理未匹配的情况 |
第三部分:条件表达式与模式
3.1 三元运算符在条件赋值中的应用
语法与C++相同:condition ? valueIfTrue : valueIfFalse
// IoT场景:根据温度决定警告级别
let temperature: number = 92.5;
let warningLevel: string = temperature > 90.0 ? "高温警告" : "正常";
console.log(warningLevel); // "高温警告"
// 嵌套三元运算符(控制嵌套层数,保持可读性)
let alertLevel: string =
temperature > 100.0 ? "紧急" :
temperature > 90.0 ? "警告" :
temperature > 80.0 ? "注意" : "正常";
console.log(alertLevel); // "警告"
⚠️ 建议:嵌套超过2层时,优先使用 if-else 以提高可读性。
3.2 短路求值(&& 和 ||)的条件执行
利用短路特性实现条件执行:
// IoT场景:条件执行函数调用
let isReady: boolean = true;
let hasError: boolean = false;
// && 短路:左侧为 false,右侧不执行
isReady && console.log("设备就绪,开始处理"); // 会执行
hasError && console.log("记录错误日志"); // 不会执行
// 等同于
if (isReady) {
console.log("设备就绪,开始处理");
}
// || 短路:左侧为 true,右侧不执行
let deviceName: string = "";
let displayName: string = deviceName || "未命名设备";
console.log(displayName); // "未命名设备"
// ⚠️ 注意:0 和 "" 也是 falsy,可能触发默认值
let packetRate: number = 0;
let rate: number = packetRate || 1000;
console.log(`${rate}`); // "1000"(0 被替换,可能不是预期行为!)
3.3 空值处理模式(?? 和 ?. 配合条件判断)
?? 空值合并运算符:仅对 null 和 undefined 生效。
// IoT场景:传感器读数可能为 null
let rawReading: number | null = null;
// ✅ ?? 只对 null/undefined 生效
let reading1: number = rawReading ?? -1;
console.log(`${reading1}`); // "-1"
// 当传感器读到 0(合法值)
let rawReading2: number | null = 0;
let reading2: number = rawReading2 ?? -1;
console.log(`${reading2}`); // "0"(正确保留0)
// ⚠️ 对比 ||:会把 0 也替换掉
let reading3: number = rawReading2 || -1;
console.log(`${reading3}`); // "-1"(错误!0 是合法值)
?. 可选链 + ?? 组合使用:
// IoT场景:嵌套对象安全访问
interface SensorConfig {
name: string;
calibration: {
offset: number;
scale: number;
} | null;
}
let config: SensorConfig | null = null;
// 安全获取校准参数,提供默认值
let offset: number = config?.calibration?.offset ?? 0.0;
let scale: number = config?.calibration?.scale ?? 1.0;
console.log(`offset=${offset}, scale=${scale}`); // "offset=0, scale=1"
【第三部分小结】
| 运算符/模式 | 说明 | 示例 |
|---|---|---|
?: |
三元运算符 | const x = a > b ? a : b; |
&& |
逻辑与,短路求值 | isReady && start(); |
|| |
逻辑或,短路求值 | name || "default" |
?? |
空值合并(仅null/undefined) | value ?? default |
?. |
可选链 | obj?.prop?.method() |
第四部分:类型守卫(ArkTS 重要模式)
4.1 typeof 类型守卫(联合类型分支处理)
当函数参数是联合类型时,使用 typeof 检查来区分类型:
// IoT场景:处理不同类型的传感器ID
function processSensorId(id: string | number): string {
// typeof 类型守卫
if (typeof id === "string") {
// 在这个分支中,id 被收窄为 string 类型
return `字符串ID: ${id.toUpperCase()}`;
} else {
// 在这个分支中,id 被收窄为 number 类型
return `数字ID: ${id.toFixed(0)}`;
}
}
console.log(processSensorId("sensor_001")); // "字符串ID: SENSOR_001"
console.log(processSensorId(1001)); // "数字ID: 1001"
⚠️ 重要限制:类型守卫主要用于函数参数。对于局部变量,如果实际类型已确定,else 分支可能被推断为 never:
let id: string | number = "sensor_001"; // 实际就是 string
if (typeof id === "string") {
// id 是 string
} else {
// ❌ 这里 id 是 never,不是 number
// 因为 ArkTS 知道 id 一定是 "sensor_001"(string)
}
typeof 返回值:
| 值类型 | typeof 返回 |
|---|---|
number |
"number" |
string |
"string" |
boolean |
"boolean" |
undefined |
"undefined" |
null |
"object" ⚠️ |
| 数组 | "object" |
4.2 instanceof 类型守卫(类继承分支处理)
检查对象是否是某个类的实例:
// IoT场景:不同类型的传感器处理
class Sensor {
name: string = "Generic Sensor";
read(): string {
return "基础传感器读数";
}
}
class TemperatureSensor extends Sensor {
temperature: number = 25.0;
read(): string {
return `温度: ${this.temperature}°C`;
}
}
class HumiditySensor extends Sensor {
humidity: number = 60.0;
read(): string {
return `湿度: ${this.humidity}%`;
}
}
function processSensor(sensor: Sensor): string {
if (sensor instanceof TemperatureSensor) {
// sensor 被收窄为 TemperatureSensor
return `温度传感器 - ${sensor.read()}`;
} else if (sensor instanceof HumiditySensor) {
// sensor 被收窄为 HumiditySensor
return `湿度传感器 - ${sensor.read()}`;
} else {
return `未知传感器 - ${sensor.read()}`;
}
}
let tempSensor: TemperatureSensor = new TemperatureSensor();
console.log(processSensor(tempSensor)); // "温度传感器 - 温度: 25°C"
⚠️ 限制:instanceof 左操作数必须是引用类型(对象、数组、类实例),基础类型(number/string/boolean)不能使用。
4.3 null/undefined 检查与类型收窄
显式检查 null 或 undefined,编译器会自动收窄类型:
// IoT场景:处理可能为空的传感器数据
function calculateAverage(readings: number[] | null): string {
// null 检查实现类型收窄
if (readings === null) {
return "无数据";
}
// 在这个分支中,readings 被收窄为 number[]
if (readings.length === 0) {
return "数据为空数组";
}
let sum: number = 0;
for (let i: number = 0; i < readings.length; i++) {
sum += readings[i];
}
let avg: number = sum / readings.length;
return `平均值: ${avg.toFixed(2)}`;
}
console.log(calculateAverage(null)); // "无数据"
console.log(calculateAverage([])); // "数据为空数组"
console.log(calculateAverage([20, 25, 30])); // "平均值: 25.00"
4.4 ArkTS 流敏感类型检查
ArkTS 编译器支持流敏感类型检查——在 if 分支内,经过类型检查后,编译器会自动收窄变量类型。
// IoT场景:联合类型的流敏感类型收窄
function processValue(value: string | number | null): string {
// 首先检查 null
if (value === null) {
return "值为空";
}
// 此时 value 被收窄为 string | number
if (typeof value === "string") {
// 此时 value 被收窄为 string
return `字符串长度: ${value.length}`;
}
// 此时 value 被收窄为 number
return `数字平方: ${value * value}`;
}
console.log(processValue(null)); // "值为空"
console.log(processValue("hello")); // "字符串长度: 5"
console.log(processValue(5)); // "数字平方: 25"
⚠️ 重要限制:跨分支类型比较
ArkTS 中不同类型的变量不能直接用 === 比较:
let a: number = 42;
let b: string = "42";
// ❌ 编译错误:number 和 string 无交集,不能用 === 比较
// console.log(a === b);
// ✅ 必须先转换为相同类型
console.log(`${a}` === b); // "true"
console.log(a === Number(b)); // true
联合类型与具体类型的比较:
function checkValue(value: string | number): void {
// ✅ 合法:value 与 "42" 有交集(string 部分)
if (value === "42") {
console.log("字符串42");
}
// ✅ 合法:value 与 42 有交集(number 部分)
if (value === 42) {
console.log("数字42");
}
}
【第四部分小结】
| 类型守卫 | 用途 | 示例 |
|---|---|---|
typeof |
区分基础类型 | typeof x === "string" |
instanceof |
区分类实例 | x instanceof MyClass |
=== null |
检查空值 | if (x === null) |
=== undefined |
检查未定义 | if (x === undefined) |
| 流敏感检查 | 自动类型收窄 | 编译器在分支内自动收窄 |
第五部分:实际应用案例
5.1 设备状态机(enum + switch)
// IoT场景:智能灯设备状态机
enum LightState {
OFF = "OFF",
ON = "ON",
DIMMING = "DIMMING",
ERROR = "ERROR"
}
interface LightDevice {
id: string;
state: LightState;
brightness: number;
}
function handleLightState(device: LightDevice): string {
switch (device.state) {
case LightState.OFF:
return `灯${device.id}已关闭`;
case LightState.ON:
return `灯${device.id}已开启,亮度${device.brightness}%`;
case LightState.DIMMING:
return `灯${device.id}调光中,目标亮度${device.brightness}%`;
case LightState.ERROR:
return `灯${device.id}故障,请检查`;
default:
// 防御性编程
return `灯${device.id}未知状态`;
}
}
// 使用示例
let livingRoomLight: LightDevice = {
id: "LIVING_ROOM_01",
state: LightState.ON,
brightness: 80
};
console.log(handleLightState(livingRoomLight)); // "灯LIVING_ROOM_01已开启,亮度80%"
5.2 传感器数据校验(综合条件模式 + 类型守卫)
// IoT场景:传感器数据校验与处理
interface SensorReading {
sensorId: string;
value: number;
unit: string;
timestamp: number;
}
interface ValidationResult {
valid: boolean;
message: string;
reading?: SensorReading;
}
function validateSensorData(
data: unknown
): ValidationResult {
// 首先检查是否为对象
if (data === null || typeof data !== "object") {
return { valid: false, message: "数据必须是对象" };
}
// 类型收窄为对象后检查属性
let obj = data as Record<string, unknown>;
// 检查必需字段
if (typeof obj.sensorId !== "string" || obj.sensorId === "") {
return { valid: false, message: "sensorId 必须是有效字符串" };
}
if (typeof obj.value !== "number" || isNaN(obj.value)) {
return { valid: false, message: "value 必须是有效数字" };
}
if (typeof obj.unit !== "string") {
return { valid: false, message: "unit 必须是字符串" };
}
if (typeof obj.timestamp !== "number") {
return { valid: false, message: "timestamp 必须是数字" };
}
// 构建有效结果
let reading: SensorReading = {
sensorId: obj.sensorId,
value: obj.value,
unit: obj.unit,
timestamp: obj.timestamp
};
return { valid: true, message: "数据有效", reading: reading };
}
// 使用示例
let rawData = {
sensorId: "TEMP_001",
value: 25.5,
unit: "°C",
timestamp: Date.now()
};
let result: ValidationResult = validateSensorData(rawData);
if (result.valid && result.reading !== undefined) {
console.log(`传感器 ${result.reading.sensorId} 读数: ${result.reading.value}${result.reading.unit}`);
} else {
console.log(`校验失败: ${result.message}`);
}
测试题
题1:判断输出
以下代码输出什么?
let a: number = 0;
let b: string = "";
let c: string = "hello";
let d: number = 5;
if (a) {
console.log("A");
} else if (b) {
console.log("B");
} else if (c) {
console.log("C");
} else if (d) {
console.log("D");
} else {
console.log("E");
}
答案:输出 C。解析:a 是 0(falsy),b 是空字符串(falsy),c 是非空字符串(truthy),所以执行 console.log("C")。
题2:判断输出
以下代码输出什么?
let value: string | number = "test";
let result: string;
if (typeof value === "string") {
result = `str:${value}`;
} else {
result = `num:${value * 2}`;
}
console.log(result);
答案:输出 str:test
解析:value 实际赋值为 "test"(string),所以进入 typeof value === "string" 分支。注意:这个例子中类型守卫在局部变量上的行为和函数参数不同,else 分支实际上不可达。
题3:找错
以下代码有哪些 ArkTS 编译错误?请指出并修正。
// 代码A
let x: number = 10;
let y: string = "10";
console.log(x === y);
// 代码B
let count: number = 5;
console.log(count);
答案与修正:
// 代码A:number 和 string 不能直接用 === 比较
// ❌ console.log(x === y);
// ✅ 修正:转换为相同类型
console.log(`${x}` === y); // "true"
// 代码B:console.log 只接受 string 参数
// ❌ console.log(count);
// ✅ 修正:转换为 string
console.log(`${count}`);
题4:代码分析
以下代码有什么问题?如何修正?
let input: string | number = 100;
let result: string;
if (input) {
result = input.toUpperCase();
}
答案与修正:
// ❌ 错误:在 input 为 truthy 的分支中,input 仍是 string | number 联合类型
// number 类型没有 toUpperCase 方法
// ✅ 修正:使用 typeof 检查具体类型
let input: string | number = 100;
let result: string;
if (typeof input === "string") {
result = input.toUpperCase();
} else {
result = `${input}`;
}
注意:这个修正仅适用于演示。实际中,局部变量应该直接声明为具体类型 let input: number = 100;,不需要类型守卫。
题5:编程题
使用 switch 语句处理设备命令:
要求:
- 声明变量
command: string = "POWER_ON" - 使用 switch 语句处理以下命令:
"POWER_ON":输出"设备已开启""POWER_OFF":输出"设备已关闭""STATUS":输出"设备状态正常"- 其他:输出
"未知命令"
参考答案:
let command: string = "POWER_ON";
let result: string;
switch (command) {
case "POWER_ON":
result = "设备已开启";
break;
case "POWER_OFF":
result = "设备已关闭";
break;
case "STATUS":
result = "设备状态正常";
break;
default:
result = "未知命令";
break;
}
console.log(result);
题6:编程题
使用三元运算符和空值合并:
要求:
- 声明变量
value: number | null = null - 使用
??运算符提供默认值-1 - 使用三元运算符判断:如果值大于 0 输出
"正数",否则输出"非正数"
参考答案:
let value: number | null = null;
// 使用 ?? 提供默认值
let actualValue: number = value ?? -1;
// 使用三元运算符判断
let result: string = actualValue > 0 ? "正数" : "非正数";
console.log(result); // "非正数"(因为 value 是 null,?? 返回 -1)
扩展练习:尝试将 value 分别设为 null、0、5,观察输出变化。
条件控制速查表
| 语法/概念 | 代码示例 | 说明 |
|---|---|---|
| if 语句 | if (x > 0) { } |
条件为 truthy 时执行 |
| if-else | if (x) { } else { } |
二选一执行 |
| if-else if | if (a) { } else if (b) { } |
多分支选择 |
| switch | switch (x) { case v: ... } |
多值匹配 |
| fall-through | case A: case B: ... break; |
多个 case 共享代码 |
| 三元运算符 | cond ? a : b |
简单条件赋值 |
| 短路 && | ready && start() |
条件执行 |
| 短路 || | name || "default" |
默认值(注意 falsy) |
| 空值合并 ?? | value ?? default |
仅对 null/undefined 生效 |
| 可选链 ?. | obj?.prop?.method() |
安全访问嵌套属性 |
| typeof 守卫 | typeof x === "string" |
区分基础类型 |
| instanceof | x instanceof Class |
区分类实例 |
| null 检查 | if (x !== null) |
类型收窄为具体类型 |
truthy/falsy 速查:
| 值 | 布尔结果 |
|---|---|
0 |
falsy |
| 非零数字 | truthy |
"" |
falsy |
| 非空字符串 | truthy |
null |
falsy |
undefined |
falsy |
true |
truthy |
false |
falsy |

浙公网安备 33010602011771号