Day 05 - 数组和元组深入

目标:掌握ArkTS数组的声明、操作、高级方法和元组类型
预计时间:2-3小时


第一部分:数组声明与基本操作

1.1 声明方式(类型[] 和 Array<类型>)

ArkTS提供两种等价的数组声明语法:

// 方式一:类型[](推荐,更简洁)
let deviceIds: number[] = [1001, 1002, 1003, 1004];
let deviceNames: string[] = ["温度传感器-01", "湿度传感器-02", "光照传感器-03"];
let deviceStatuses: boolean[] = [true, false, true];

// 方式二:Array<类型>(泛型写法)
let voltages: Array<number> = [220, 380, 110, 220];
let sensorTypes: Array<string> = ["TEMP", "HUMI", "LIGHT", "PRESS"];

⚠️ ArkTS 空数组必须显式声明类型

// ⚠️ 推断为 never[],无法添加元素
// let emptyDevices = [];  // 类型为 never[],后续无法 push

// ✅ 正确:显式声明类型
let emptyDevices: string[] = [];
let emptyValues: Array<number> = [];
let sensorReadings: number[] = [];  // 准备存储传感器读数

1.2 多维数组

二维数组常用于存储矩阵型数据,如传感器网格读数:

// 二维数组:3x3传感器网格的温度读数
let temperatureGrid: number[][] = [
  [25.5, 26.0, 24.8],
  [26.2, 27.1, 25.9],
  [24.5, 25.3, 26.7]
];

// 访问元素:第2行第3列(0-based索引)
let centerTemp: number = temperatureGrid[1][2];  // 25.9
console.log(`中心区域温度:${centerTemp}°C`);

// 修改元素
temperatureGrid[0][0] = 26.5;

// 三维数组:多层楼宇传感器数据
let buildingSensors: number[][][] = [
  [  // 第1层
    [22.5, 23.0],  // 房间1的温度、湿度
    [23.5, 45.0]   // 房间2的温度、湿度
  ],
  [  // 第2层
    [24.0, 42.0],
    [25.5, 48.0]
  ]
];

// 访问:第2层第1个房间的温度
let roomTemp: number = buildingSensors[1][0][0];  // 24.0
console.log(`2层1号房间温度:${roomTemp}°C`);

1.3 访问与修改(越界返回undefined,不崩溃)

let devices: string[] = ["DEV-001", "DEV-002", "DEV-003"];

// 访问元素(0-based索引)
let firstDevice: string = devices[0];   // "DEV-001"
let secondDevice: string = devices[1];  // "DEV-002"

// 修改元素
devices[1] = "DEV-002-UPDATED";
console.log(`更新后的设备列表:${devices.join(", ")}`);

// 越界访问(不会崩溃,返回undefined)
let outOfBound: string | undefined = devices[10];
console.log(`越界访问结果:${outOfBound}`);  // "undefined"

// 安全访问模式
let index: number = 5;
if (index < devices.length) {
  console.log(`设备:${devices[index]}`);
} else {
  console.log(`索引${index}超出范围,数组长度为${devices.length}`);
}

⚠️ 与C++的重要区别:C++数组越界是未定义行为(可能崩溃),ArkTS数组越界安全地返回undefined


1.4 获取长度(.length 是属性不是方法)

let sensorData: number[] = [23.5, 24.0, 22.8, 25.2];

// length是属性,不是方法,不需要括号
let count: number = sensorData.length;  // 4
console.log(`传感器数据点数量:${count}`);

// 实际应用:计算平均值
let sum: number = 0;
for (let i: number = 0; i < sensorData.length; i++) {
  sum += sensorData[i];
}
let average: number = sum / sensorData.length;
console.log(`平均温度:${average.toFixed(2)}°C`);

// 空数组检查
let pendingDevices: string[] = [];
if (pendingDevices.length === 0) {
  console.log("没有待处理设备");
}

【小结表格】

操作 语法 示例 说明
声明数组(推荐) 类型[] let arr: number[] = [1, 2, 3] -
声明数组(泛型) Array<类型> let arr: Array<number> = [1, 2, 3] -
声明空数组 必须显式类型 let arr: string[] = [] 否则推断为never[]
访问元素 arr[index] let x: number = arr[0] -
修改元素 arr[index] = value arr[0] = 100 -
获取长度 arr.length let n: number = arr.length 属性,不是方法
越界访问 返回undefined arr[100]undefined 安全,不崩溃

第二部分:数组增删操作

2.1 push/pop(末尾增删)

let deviceQueue: string[] = ["DEV-A", "DEV-B"];

// push:在末尾添加一个或多个元素
// ArkTS支持多参数push
deviceQueue.push("DEV-C");                    // ["DEV-A", "DEV-B", "DEV-C"]
deviceQueue.push("DEV-D", "DEV-E");           // ["DEV-A", "DEV-B", "DEV-C", "DEV-D", "DEV-E"]
console.log(`添加后队列:${deviceQueue.join(", ")}`);

// pop:删除并返回末尾元素
let lastDevice: string | undefined = deviceQueue.pop();  // "DEV-E"
console.log(`移除的设备:${lastDevice}`);
console.log(`剩余队列:${deviceQueue.join(", ")}`);

// 空数组pop返回undefined
let emptyArr: string[] = [];
let result: string | undefined = emptyArr.pop();  // undefined
console.log(`空数组pop结果:${result}`);

2.2 unshift/shift(开头增删)

let taskQueue: string[] = ["任务2", "任务3"];

// unshift:在开头添加一个或多个元素
taskQueue.unshift("任务1");           // ["任务1", "任务2", "任务3"]
taskQueue.unshift("紧急任务", "优先任务");  // ["紧急任务", "优先任务", "任务1", "任务2", "任务3"]
console.log(`任务队列:${taskQueue.join(" → ")}`);

// shift:删除并返回开头元素(FIFO队列)
let currentTask: string | undefined = taskQueue.shift();  // "紧急任务"
console.log(`当前执行任务:${currentTask}`);
console.log(`剩余任务:${taskQueue.join(" → ")}`);

// IoT场景:处理传感器数据流
let sensorBuffer: number[] = [];
sensorBuffer.push(23.5);  // 新数据入队
sensorBuffer.push(24.0);
sensorBuffer.push(22.8);

while (sensorBuffer.length > 0) {
  let reading: number | undefined = sensorBuffer.shift();
  if (reading !== undefined) {
    console.log(`处理读数:${reading}°C`);
  }
}

⚠️ 性能提示unshiftshift需要移动所有现有元素,对于大数组效率较低。频繁在开头操作可考虑使用其他数据结构。


2.3 splice(删除/插入元素)

let deviceList: string[] = ["DEV-001", "DEV-002", "DEV-003", "DEV-004", "DEV-005"];

// splice(start, deleteCount, ...items):从start位置删除deleteCount个元素,并可插入新元素
// 返回被删除的元素数组

// 从索引2开始删除1个元素
let removed: string[] = deviceList.splice(2, 1);
console.log(`被删除:${removed.join(", ")}`);  // "DEV-003"
console.log(`剩余:${deviceList.join(", ")}`);  // ["DEV-001", "DEV-002", "DEV-004", "DEV-005"]

// 从索引1开始删除2个元素
let removed2: string[] = deviceList.splice(1, 2);
console.log(`被删除:${removed2.join(", ")}`);  // "DEV-002, DEV-004"
console.log(`剩余:${deviceList.join(", ")}`);  // ["DEV-001", "DEV-005"]

// 在索引1处插入元素(删除0个,插入新元素)
let arr: number[] = [10, 20, 30, 50];
arr.splice(2, 0, 25);  // 在索引2处插入25
console.log(`插入后:${arr.join(", ")}`);  // [10, 20, 25, 30, 50]

// 从末尾删除:使用负数索引
let arr2: number[] = [10, 20, 30, 40, 50];
arr2.splice(-2, 1);  // 删除倒数第2个元素(40)
console.log(`结果:${arr2.join(", ")}`);  // [10, 20, 30, 50]

2.4 concat(拼接数组)

let onlineDevices: string[] = ["DEV-A", "DEV-B"];
let offlineDevices: string[] = ["DEV-C", "DEV-D"];
let newDevices: string[] = ["DEV-E"];

// concat:合并多个数组,不修改原数组
let allDevices: string[] = onlineDevices.concat(offlineDevices);
console.log(`合并后:${allDevices.join(", ")}`);

// 可以链式拼接多个数组
let completeList: string[] = onlineDevices.concat(offlineDevices, newDevices);
console.log(`完整列表:${completeList.join(", ")}`);

// 原数组保持不变
console.log(`onlineDevices未改变:${onlineDevices.join(", ")}`);

// 添加单个元素
let withNew: string[] = onlineDevices.concat("DEV-F");
console.log(`添加单个:${withNew.join(", ")}`);

2.5 slice(提取子数组)

let sensorReadings: number[] = [22.5, 23.0, 24.5, 25.0, 26.5, 27.0];

// slice(start, end?):提取从start到end(不包含)的子数组
// 不修改原数组

// 提取索引1到3(不包含3)
let subset1: number[] = sensorReadings.slice(1, 4);  // [23.0, 24.5, 25.0]
console.log(`子数组1:${subset1.join(", ")}`);

// 从索引2到末尾
let subset2: number[] = sensorReadings.slice(2);  // [24.5, 25.0, 26.5, 27.0]
console.log(`子数组2:${subset2.join(", ")}`);

// 负数索引:从末尾计数
let lastTwo: number[] = sensorReadings.slice(-2);   // [26.5, 27.0]
let middle: number[] = sensorReadings.slice(1, -1);  // [23.0, 24.5, 25.0, 26.5]
console.log(`最后两个:${lastTwo.join(", ")}`);
console.log(`去掉首尾:${middle.join(", ")}`);

// 原数组保持不变
console.log(`原数组:${sensorReadings.join(", ")}`);

【小结表格】

方法 作用 修改原数组 返回值
push(...items) 末尾添加 ✅ 是 新长度
pop() 删除末尾 ✅ 是 被删元素/undefined
unshift(...items) 开头添加 ✅ 是 新长度
shift() 删除开头 ✅ 是 被删元素/undefined
splice(start, count, ...items) 删除/插入 ✅ 是 被删元素数组
concat(...arrays) 拼接数组 ❌ 否 新数组
slice(start?, end?) 提取子数组 ❌ 否 新数组

第三部分:数组查找与判断

3.1 indexOf / includes

let deviceIds: number[] = [1001, 1002, 1003, 1004, 1005];

// indexOf:查找元素索引,找不到返回-1
let idx1: number = deviceIds.indexOf(1003);   // 2
let idx2: number = deviceIds.indexOf(9999);   // -1(不存在)
console.log(`1003的索引:${idx1}`);
console.log(`9999的索引:${idx2}`);

// 指定起始搜索位置
let idx3: number = deviceIds.indexOf(1001, 1);  // -1(从索引1开始找,找不到1001)

// includes:判断是否包含元素
let has1002: boolean = deviceIds.includes(1002);   // true
let has9999: boolean = deviceIds.includes(9999);   // false

// IoT场景:检查设备是否在白名单中
let whitelist: number[] = [1001, 1003, 1005];
let deviceId: number = 1003;

if (whitelist.includes(deviceId)) {
  console.log(`设备${deviceId}在白名单中,允许连接`);
} else {
  console.log(`设备${deviceId}未授权,拒绝连接`);
}

3.2 find / findIndex

interface Device {
  id: number;
  name: string;
  online: boolean;
}

let devices: Device[] = [
  { id: 1001, name: "温度传感器A", online: true },
  { id: 1002, name: "湿度传感器B", online: false },
  { id: 1003, name: "光照传感器C", online: true }
];

// find:返回第一个满足条件的元素,找不到返回undefined
let onlineDevice: Device | undefined = devices.find((d: Device) => d.online === true);
if (onlineDevice !== undefined) {
  console.log(`找到在线设备:${onlineDevice.name}`);
}

// findIndex:返回第一个满足条件的索引,找不到返回-1
let offlineIdx: number = devices.findIndex((d: Device) => d.online === false);
console.log(`第一个离线设备索引:${offlineIdx}`);  // 1

// 复杂条件查找
let targetDevice: Device | undefined = devices.find((d: Device) => {
  return d.id > 1001 && d.online === true;
});
console.log(`ID>1001的在线设备:${targetDevice?.name ?? "无"}`);

3.3 some / every

interface Sensor {
  id: number;
  type: string;
  value: number;
  normal: boolean;
}

let sensors: Sensor[] = [
  { id: 1, type: "TEMP", value: 25.5, normal: true },
  { id: 2, type: "HUMI", value: 60.0, normal: true },
  { id: 3, type: "TEMP", value: 85.0, normal: false }
];

// some:是否有至少一个元素满足条件
let hasAbnormal: boolean = sensors.some((s: Sensor) => s.normal === false);
console.log(`是否有异常传感器:${hasAbnormal}`);  // true

let hasHighTemp: boolean = sensors.some((s: Sensor) => s.type === "TEMP" && s.value > 80);
console.log(`是否有高温警报:${hasHighTemp}`);  // true

// every:是否所有元素都满足条件
let allNormal: boolean = sensors.every((s: Sensor) => s.normal === true);
console.log(`是否全部正常:${allNormal}`);  // false

let allHaveId: boolean = sensors.every((s: Sensor) => s.id > 0);
console.log(`是否都有有效ID:${allHaveId}`);  // true

// IoT场景:系统状态检查
function checkSystemStatus(sensorList: Sensor[]): string {
  if (sensorList.length === 0) {
    return "系统无传感器";
  }
  
  if (sensorList.every((s: Sensor) => s.normal)) {
    return "系统全部正常";
  }
  
  if (sensorList.some((s: Sensor) => s.normal === false && s.value > 80)) {
    return "严重警报:检测到高危异常";
  }
  
  return "系统存在轻微异常";
}

console.log(checkSystemStatus(sensors));

【小结表格】

方法 作用 返回值 找不到时
indexOf(value, from?) 查找元素索引 number -1
includes(value) 判断是否包含 boolean -
find(predicate) 查找满足条件的元素 T | undefined undefined
findIndex(predicate) 查找满足条件的索引 number -1
some(predicate) 是否有元素满足条件 boolean false(空数组)
every(predicate) 是否所有元素满足条件 boolean true(空数组)

第四部分:数组高级方法(函数式风格)

4.1 map(映射转换)

let rawReadings: number[] = [23.5, 24.0, 22.8, 25.2];

// map:对每个元素进行转换,返回新数组
// 场景:原始ADC读数转换为实际温度值
let temperatures: number[] = rawReadings.map((reading: number) => {
  return reading * 0.1 + 20;  // 模拟转换公式
});
console.log(`转换后温度:${temperatures.join(", ")}`);

// 场景:设备ID生成设备名称
let deviceIds: number[] = [101, 102, 103];
let deviceNames: string[] = deviceIds.map((id: number) => {
  return `SENSOR-${id.toString().padStart(3, "0")}`;
});
console.log(`设备名称:${deviceNames.join(", ")}`);
// 输出:SENSOR-101, SENSOR-102, SENSOR-103

// 带索引的map
let indexedNames: string[] = deviceIds.map((id: number, index: number) => {
  return `[${index}] DEVICE-${id}`;
});
console.log(`${indexedNames.join("; ")}`);

4.2 filter(过滤)

interface DeviceStatus {
  id: number;
  name: string;
  temperature: number;
  online: boolean;
}

let allDevices: DeviceStatus[] = [
  { id: 1, name: "DEV-A", temperature: 25.0, online: true },
  { id: 2, name: "DEV-B", temperature: 85.0, online: true },
  { id: 3, name: "DEV-C", temperature: 30.0, online: false },
  { id: 4, name: "DEV-D", temperature: 90.0, online: true }
];

// filter:筛选满足条件的元素
// 场景:找出高温设备(>80度)
let highTempDevices: DeviceStatus[] = allDevices.filter((d: DeviceStatus) => {
  return d.temperature > 80.0;
});
console.log(`高温设备数量:${highTempDevices.length}`);
highTempDevices.forEach((d: DeviceStatus) => {
  console.log(`  - ${d.name}: ${d.temperature}°C`);
});

// 场景:找出在线且正常的设备
let healthyDevices: DeviceStatus[] = allDevices.filter((d: DeviceStatus) => {
  return d.online === true && d.temperature < 80.0;
});
console.log(`健康设备:${healthyDevices.map((d: DeviceStatus) => d.name).join(", ")}`);

// 场景:过滤掉无效数据
let readings: (number | null)[] = [23.5, null, 24.0, null, 25.5];
let validReadings: number[] = readings.filter((r): r is number => r !== null);
console.log(`有效读数:${validReadings.join(", ")}`);

4.3 reduce(归约)

let powerReadings: number[] = [120, 135, 128, 142, 130];

// reduce:将数组归约为单个值
// 语法:arr.reduce((累加器, 当前值, 当前索引) => 新累加器, 初始值)

// 场景:计算总功耗
let totalPower: number = powerReadings.reduce((sum: number, current: number) => {
  return sum + current;
}, 0);
console.log(`总功耗:${totalPower}W`);

// 场景:计算平均值
let averagePower: number = powerReadings.reduce((sum: number, current: number) => {
  return sum + current;
}, 0) / powerReadings.length;
console.log(`平均功耗:${averagePower.toFixed(1)}W`);

// 场景:找出最大值
let maxPower: number = powerReadings.reduce((max: number, current: number) => {
  return current > max ? current : max;
}, 0);
console.log(`峰值功耗:${maxPower}W`);

// 场景:统计各类设备数量
interface Device {
  id: number;
  type: string;
}

let deviceList: Device[] = [
  { id: 1, type: "TEMP" },
  { id: 2, type: "HUMI" },
  { id: 3, type: "TEMP" },
  { id: 4, type: "LIGHT" },
  { id: 5, type: "TEMP" }
];

let typeCount: Record<string, number> = deviceList.reduce((acc: Record<string, number>, d: Device) => {
  acc[d.type] = (acc[d.type] ?? 0) + 1;
  return acc;
}, {} as Record<string, number>);

console.log(`设备统计:TEMP=${typeCount["TEMP"]}, HUMI=${typeCount["HUMI"]}, LIGHT=${typeCount["LIGHT"]}`);

4.4 forEach(遍历)

let sensorNames: string[] = ["温度传感器", "湿度传感器", "光照传感器"];

// forEach:遍历每个元素,无返回值
sensorNames.forEach((name: string, index: number) => {
  console.log(`[${index}] 正在初始化:${name}`);
});

// IoT场景:批量发送指令
let deviceAddresses: number[] = [0x01, 0x02, 0x03, 0x04];
deviceAddresses.forEach((addr: number) => {
  // 模拟发送读取指令
  console.log(`向地址0x${addr.toString(16).padStart(2, "0")}发送读取指令`);
});

// 注意:forEach中不能使用break/continue
// 如需中途停止,请使用for循环

4.5 链式调用(filter + map + reduce 组合)

interface SensorData {
  id: number;
  type: string;
  value: number;
  timestamp: number;
}

let rawData: SensorData[] = [
  { id: 1, type: "TEMP", value: 25.5, timestamp: 1000 },
  { id: 2, type: "TEMP", value: 26.0, timestamp: 1001 },
  { id: 3, type: "HUMI", value: 60.0, timestamp: 1002 },
  { id: 4, type: "TEMP", value: 85.0, timestamp: 1003 },
  { id: 5, type: "TEMP", value: 24.5, timestamp: 1004 }
];

// 场景:计算过去一小时内温度传感器的平均温度(排除异常值>80)
let avgTemp: number = rawData
  .filter((d: SensorData) => d.type === "TEMP")        // 只取温度数据
  .filter((d: SensorData) => d.value <= 80.0)          // 排除异常值
  .map((d: SensorData) => d.value)                     // 提取温度值
  .reduce((sum: number, val: number, idx: number, arr: number[]) => {
    return idx === arr.length - 1 ? (sum + val) / arr.length : sum + val;
  }, 0);

console.log(`平均温度:${avgTemp.toFixed(2)}°C`);

// 场景:生成设备状态报告
interface DeviceReport {
  deviceId: string;
  status: string;
  reading: number;
}

let reports: DeviceReport[] = rawData
  .filter((d: SensorData) => d.type === "TEMP")
  .map((d: SensorData): DeviceReport => {
    return {
      deviceId: `TEMP-${d.id}`,
      status: d.value > 80.0 ? "ALERT" : "NORMAL",
      reading: d.value
    };
  });

reports.forEach((r: DeviceReport) => {
  console.log(`${r.deviceId}: ${r.status} - ${r.reading}°C`);
});

【小结表格】

方法 作用 返回值 修改原数组
map(callback) 映射转换 新数组 ❌ 否
filter(predicate) 过滤筛选 新数组 ❌ 否
reduce(callback, init) 归约聚合 单个值 ❌ 否
forEach(callback) 遍历执行 undefined ❌ 否

第五部分:只读数组

5.1 readonly 修饰符

// readonly修饰符:数组内容不可修改
const DEVICE_TYPES: readonly string[] = ["LED", "RGB", "DIMMABLE", "SWITCH"];

// ❌ 编译错误:无法修改只读数组
// DEVICE_TYPES[0] = "OTHER";
// DEVICE_TYPES.push("NEW");
// DEVICE_TYPES.pop();
// DEVICE_TYPES.splice(0, 1);

// ✅ 允许的操作:读取
console.log(`支持的设备类型:${DEVICE_TYPES.join(", ")}`);
console.log(`类型数量:${DEVICE_TYPES.length}`);

// ✅ 允许的操作:非修改方法
let hasLed: boolean = DEVICE_TYPES.includes("LED");  // true
let index: number = DEVICE_TYPES.indexOf("RGB");     // 1

// ✅ 允许的操作:创建新数组
let upperTypes: string[] = DEVICE_TYPES.map((t: string) => t.toUpperCase());

5.2 ReadonlyArray 泛型

// ReadonlyArray<T>:另一种只读数组声明方式
const VOLTAGE_LEVELS: ReadonlyArray<number> = [110, 220, 380];

// 与readonly修饰符效果相同
// VOLTAGE_LEVELS.push(440);  // ❌ 编译错误

// 常用于函数参数,防止函数修改传入的数组
function calculateAverage(readings: ReadonlyArray<number>): number {
  // readings.push(999);  // ❌ 编译错误,安全!
  
  let sum: number = 0;
  for (let i: number = 0; i < readings.length; i++) {
    sum += readings[i];
  }
  return readings.length > 0 ? sum / readings.length : 0;
}

let temps: number[] = [23.5, 24.0, 22.8];
let avg: number = calculateAverage(temps);  // 可以传入普通数组
console.log(`平均值:${avg.toFixed(2)}`);

// 从普通数组创建只读数组
let mutableArr: number[] = [1, 2, 3];
let readonlyArr: ReadonlyArray<number> = mutableArr;
// readonlyArr.push(4);  // ❌ 编译错误

【小结表格】

声明方式 语法 用途
readonly修饰符 readonly T[] 常量数组定义
ReadonlyArray泛型 ReadonlyArray<T> 函数参数保护
禁止的操作 push/pop/splice等修改方法 -
允许的操作 读取、遍历、非修改方法 -

第六部分:元组(Tuple)

6.1 元组声明与访问(通过索引,不能解构!)

元组是固定长度数组,每个位置有确定的类型:

// 元组声明:[类型1, 类型2, 类型3, ...]
let deviceInfo: [number, string, boolean] = [1001, "温度传感器-A", true];
//        索引:   0       1          2
//        类型: number  string    boolean

// 通过索引访问(类型已知)
let deviceId: number = deviceInfo[0];      // 1001,类型number
let deviceName: string = deviceInfo[1];    // "温度传感器-A",类型string
let isOnline: boolean = deviceInfo[2];     // true,类型boolean

console.log(`设备${deviceId}(${deviceName})在线状态:${isOnline}`);

// IoT场景:传感器数据点 [时间戳, 数值, 质量标志]
let dataPoint: [number, number, boolean] = [Date.now(), 25.5, true];
let timestamp: number = dataPoint[0];
let value: number = dataPoint[1];
let quality: boolean = dataPoint[2];
console.log(`[${timestamp}] 读数:${value},质量:${quality ? "良好" : "可疑"}`);

// ⚠️ ArkTS不支持元组解构
// ❌ 编译错误
// let [dId, dName, dStatus] = deviceInfo;

// ✅ 正确:通过索引逐个访问
let dId: number = deviceInfo[0];
let dName: string = deviceInfo[1];
let dStatus: boolean = deviceInfo[2];
console.log(`解构效果:${dId}, ${dName}, ${dStatus}`);

6.2 可选元素

// 可选元素语法:[类型1, 类型2, 类型3?]
// ?表示该元素可以不存在

// IoT场景:设备信息 [ID, 名称, 可选:最后通信时间戳]
let deviceWithTime: [number, string, number?] = [1001, "传感器-A"];
// 也可以提供可选元素
let deviceWithTime2: [number, string, number?] = [1002, "传感器-B", Date.now()];

// 访问可选元素需要检查
let lastComm: number | undefined = deviceWithTime[2];
if (lastComm !== undefined) {
  console.log(`最后通信:${lastComm}`);
} else {
  console.log("无通信记录");
}

// 多个可选元素
let sensorConfig: [string, number?, number?, boolean?] = ["TEMP"];
// 可以只提供部分可选元素
let sensorConfig2: [string, number?, number?, boolean?] = ["HUMI", 0, 100];

6.3 只读元组

// readonly修饰元组
const SENSOR_RANGE: readonly [number, number] = [0, 100];  // 量程0-100

// ❌ 编译错误:无法修改只读元组
// SENSOR_RANGE[0] = -50;
// SENSOR_RANGE.push(200);  // 元组没有push方法

// 应用场景:设备配置常量
const DEVICE_CONFIG: readonly [string, number, boolean] = ["TEMP_SENSOR", 1000, true];
// [设备类型, 采样间隔(ms), 是否启用]

console.log(`设备类型:${DEVICE_CONFIG[0]}`);
console.log(`采样间隔:${DEVICE_CONFIG[1]}ms`);
console.log(`启用状态:${DEVICE_CONFIG[2]}`);

// 从函数返回多个值(元组方式)
function getDeviceStats(): [number, number, boolean] {
  // 模拟获取设备统计
  return [100, 99.5, true];  // [设备数, 在线率, 系统正常]
}

let stats: [number, number, boolean] = getDeviceStats();
console.log(`设备数:${stats[0]},在线率:${stats[1]}%,系统正常:${stats[2]}`);

⚠️ ArkTS 不支持元组解构

let coords: [number, number, number] = [100, 200, 50];  // x, y, z

// ❌ 编译错误:ArkTS不支持解构赋值
// let [x, y, z] = coords;

// ✅ 正确:逐个索引访问
let x: number = coords[0];
let y: number = coords[1];
let z: number = coords[2];
console.log(`坐标:(${x}, ${y}, ${z})`);

【小结表格】

特性 语法 说明
元组声明 [T1, T2, T3] 固定长度,各位置类型确定
访问元素 tuple[index] 通过索引访问
可选元素 [T1, T2?] 末尾元素可省略
只读元组 readonly [T1, T2] 内容不可修改
⚠️ 不支持 解构赋值 let [a,b]=tuple 编译错误

第七部分:ArkTS 数组限制(重要提醒)

7.1 空数组必须显式声明类型

// ❌ 编译错误:无法推断空数组类型
// let empty = [];

// ✅ 正确:显式声明类型
let emptyNumbers: number[] = [];
let emptyStrings: Array<string> = [];
let emptyDevices: Device[] = [];  // Device是interface

替代方案:声明时赋予初始类型明确的元素

// 如果不确定内容,可以先声明类型再push
let readings: number[] = [];
readings.push(23.5);  // 后续添加元素

7.2 不支持解构赋值

// ❌ 编译错误:ArkTS不支持数组解构
// let [a, b] = [1, 2];

// ❌ 编译错误:ArkTS不支持元组解构
// let device: [number, string] = [1001, "DEV-A"];
// let [id, name] = device;

// ✅ 正确:逐个索引访问
let arr: number[] = [1, 2];
let a: number = arr[0];
let b: number = arr[1];

let device: [number, string] = [1001, "DEV-A"];
let id: number = device[0];
let name: string = device[1];

7.4 禁止使用 any 和 unknown

// ❌ 编译错误:ArkTS禁止any类型
// let mixed: any[] = [1, "two", true];

// ❌ 编译错误:ArkTS禁止unknown类型
// let unknownArr: unknown[] = [];

// ✅ 正确:使用联合类型
let mixed: (number | string | boolean)[] = [1, "two", true];

// ✅ 正确:使用具体类型
interface DataPoint {
  type: string;
  value: number;
}
let dataPoints: DataPoint[] = [
  { type: "TEMP", value: 25.5 },
  { type: "HUMI", value: 60.0 }
];

测试题

题1:判断输出

let sensors: number[] = [22, 25, 28, 30];
sensors.push(32);
sensors.shift();
let result: number = sensors.reduce((a: number, b: number) => a + b, 0);
console.log(`${result}`);

问题:上述代码输出什么?

答案

输出:115

解析:

  1. 初始数组:[22, 25, 28, 30]
  2. push(32)后:[22, 25, 28, 30, 32]
  3. shift()后:[25, 28, 30, 32]
  4. reduce求和:25 + 28 + 30 + 32 = 115

题2:判断输出

let devices: string[] = ["DEV-001", "DEV-002", "DEV-003", "DEV-004"];
let sliced: string[] = devices.slice(1, 3);
let spliced: string[] = devices.splice(1, 1);
console.log(`${devices.length},${sliced.length},${spliced.length}`);

问题:上述代码输出什么?

答案

输出:3,2,1

解析:

  1. slice(1, 3):提取索引1到2的元素,返回["DEV-002", "DEV-003"],长度2,原数组不变
  2. splice(1, 1):从索引1删除1个元素,返回["DEV-002"],长度1
  3. 原数组变为["DEV-001", "DEV-003", "DEV-004"],长度3

题3:找错(ArkTS限制)

function processData(): void {
  let data = [];  // 第1处
  data.push(100);
  
  let [x, y] = [10, 20];  // 第2处
  console.log(`${x},${y}`);
}

问题:找出2处编译错误并说明原因。

答案
位置 错误 原因 修正
第1处 let data = [] 空数组推断为never[],无法添加元素 let data: number[] = []
第2处 let [x, y] = [10, 20] ArkTS不支持解构赋值 let arr: number[] = [10, 20]; let x: number = arr[0]; let y: number = arr[1]

题4:找错(类型问题)

let readings: any[] = [23.5, 24.0, 22.8];  // 第1处

let sensor: [number, string] = [1001, "TEMP"];
let [sid, stype] = sensor;  // 第2处

问题:找出2处问题并说明原因。

答案
位置 错误 原因 修正
第1处 any[] ArkTS禁止any类型 let readings: number[] = [...]
第2处 let [sid, stype] = sensor ArkTS不支持元组解构 let sid: number = sensor[0]; let stype: string = sensor[1]

题5:编程题(温度数据分析)

场景:处理一批传感器温度读数,找出异常值并生成报告。

要求

  1. 创建包含8个温度读数的数组(部分值>80表示异常高温)
  2. 使用filter找出所有异常读数(value > 80)
  3. 使用map将异常读数转换为报警信息字符串:"警报:温度XX°C超过阈值"
  4. 使用some检查是否有异常
  5. 使用every检查是否所有读数都有效(<100)
  6. 输出报警信息和检查结果
参考答案
let temperatures: number[] = [25.5, 85.0, 26.0, 92.5, 24.8, 88.0, 25.2, 95.0];

// 找出异常读数
let abnormalTemps: number[] = temperatures.filter((t: number) => t > 80.0);

// 生成报警信息
let alerts: string[] = abnormalTemps.map((t: number) => {
  return `警报:温度${t}°C超过阈值80°C`;
});

// 检查是否有异常
let hasAbnormal: boolean = temperatures.some((t: number) => t > 80.0);

// 检查是否都有效(<100)
let allValid: boolean = temperatures.every((t: number) => t < 100.0);

// 输出结果
console.log(`是否有异常:${hasAbnormal}`);
console.log(`是否全部有效(<100):${allValid}`);
console.log(`异常读数数量:${abnormalTemps.length}`);
console.log("--- 报警信息 ---");
alerts.forEach((alert: string) => {
  console.log(alert);
});

数组操作速查表

操作类别 方法 语法 返回值 修改原数组
声明 类型[] let arr: number[] = [] - -
Array<类型> let arr: Array<number> = [] - -
空数组 let arr: string[] = [] - -
增删 push arr.push(item1, item2) 新长度
pop arr.pop() 被删元素/undefined
unshift arr.unshift(item1, item2) 新长度
shift arr.shift() 被删元素/undefined
splice arr.splice(start, deleteCount) 被删元素数组
concat arr.concat(other) 新数组
slice arr.slice(start?, end?) 子数组
查找 indexOf arr.indexOf(value) number
includes arr.includes(value) boolean
find arr.find(predicate) T/undefined
findIndex arr.findIndex(predicate) number
some arr.some(predicate) boolean
every arr.every(predicate) boolean
函数式 map arr.map(callback) 新数组
filter arr.filter(predicate) 新数组
reduce arr.reduce(callback, init) 单个值
forEach arr.forEach(callback) undefined
只读 readonly readonly T[] - -
ReadonlyArray ReadonlyArray<T> - -
元组 声明 [T1, T2, T3] - -
可选 [T1, T2?] - -

ArkTS 限制速查

限制 说明 替代方案
⚠️ 空数组类型 必须显式声明类型,否则推断为never[] let arr: number[] = []
⚠️ 解构赋值 不支持数组/元组解构 逐个索引访问
⚠️ any类型 完全禁止 使用联合类型或具体类型
⚠️ unknown类型 完全禁止 使用具体类型

posted @ 2026-04-07 17:22  thammer  阅读(1)  评论(0)    收藏  举报