HarmonyOS APP<<古今职鉴定>>开源教程第3篇:ArkTS 语言基础:TypeScript 的鸿蒙进化
本篇学习 ArkTS 的核心语法,并为「古今职鉴」项目创建第一个数据模型
本篇学习 ArkTS 的核心语法,并为「古今职鉴」项目创建第一个数据模型
图:古今职鉴开源教程封面。本篇围绕「ArkTS 语言基础:TypeScript 的鸿蒙进化」展开。
学习目标
完成本篇后,你将能够:
- ✅ 理解 ArkTS 与 TypeScript 的区别
- ✅ 掌握变量、函数、接口、枚举的定义
- ✅ 了解 ArkTS 的特殊限制规则
- ✅ 为项目创建官职(Position)数据模型
预计学习时间
约 90-120 分钟
前置准备
- 已完成第2篇,DevEco Studio 环境配置完成
- 已创建
jiaocheng教程项目
---
第一部分:理解 ArkTS
1.1 ArkTS 是什么?
ArkTS 是华为为鸿蒙操作系统设计的开发语言,它基于 TypeScript 进行了扩展和限制。
ArkTS 的定位:
JavaScript (动态类型,灵活但容易出错)
↓ 添加类型系统
TypeScript (可选静态类型,更安全)
↓ 限制动态特性 + 添加UI语法
ArkTS (强制静态类型,高性能)
为什么华为要创造 ArkTS?
| 目标 | TypeScript 的问题 | ArkTS 的解决方案 |
|---|---|---|
| 类型安全 | any 类型绕过检查 |
禁止使用 any |
| 性能优化 | 动态属性无法优化 | 禁止动态属性访问 |
| 代码可维护 | 对象结构不明确 | 强制类型声明 |
| 编译优化 | 运行时类型检查 | 编译期类型检查 |
1.2 ArkTS 与 TypeScript 的主要区别
| 特性 | TypeScript | ArkTS |
|---|---|---|
| any 类型 | ✅ 允许 | ❌ 禁止 |
| 动态属性访问 | ✅ 允许 obj[key] |
❌ 禁止 |
| 对象字面量 | 可无类型 | 必须有类型声明 |
| 索引签名 | ✅ 允许 [key: string] |
❌ 禁止,用 Map 替代 |
| UI 语法 | ❌ 不支持 | ✅ 支持声明式 UI |
| 装饰器 | 实验性支持 | ✅ 原生支持 @Component 等 |
💡 记住:ArkTS 牺牲了一些灵活性,换来了更好的性能和类型安全。
---
第二部分:基础语法实战
步骤 1:创建练习目录
操作:在 DevEco Studio 中创建第3课目录
- 在项目导航器中,右键点击
products/jiaocheng/src/main/ets/ - 选择
New→Directory,输入lesson03 - 右键点击
lesson03文件夹 - 选择
New→Directory,输入models
为什么这样做:
- 按课程组织代码,便于学习和查找
models目录专门存放数据模型,遵循关注点分离原则
步骤 2:学习变量声明
操作:了解 ArkTS 中的变量声明方式
// ============================================
// 变量声明
// ============================================
// ----- 1. let 声明可变变量 -----
// let 声明的变量可以被重新赋值
let userName: string = '张三'; // 声明一个字符串变量
userName = '李四'; // ✅ 可以修改值
// ----- 2. const 声明常量 -----
// const 声明的变量不能被重新赋值
const APP_NAME: string = '古今职鉴'; // 应用名称,不会改变
// APP_NAME = '新名字'; // ❌ 错误!常量不能修改
// ----- 3. 基本数据类型 -----
let age: number = 25; // 数字类型(整数和小数都用 number)
let price: number = 99.9; // 小数也是 number
let isVip: boolean = true; // 布尔类型:true 或 false
let description: string = `${userName},年龄${age}`; // 模板字符串
类型注解说明:
| 代码 | 含义 |
|---|---|
let userName: string |
声明变量 userName,类型是 string |
: string |
类型注解,告诉编译器这个变量是什么类型 |
const APP_NAME |
常量命名习惯用大写+下划线 |
` ${变量} ` |
模板字符串,可以嵌入变量 |
步骤 3:学习数组操作
// ============================================
// 数组操作
// ============================================
// ----- 1. 声明数组 -----
// 方式一:类型[] 语法(推荐)
let dynastyNames: string[] = ['秦', '汉', '唐', '宋', '明', '清'];
// 方式二:Array<类型> 语法
let rankNumbers: Array<number> = [1, 2, 3, 4, 5, 6, 7, 8, 9];
// ----- 2. 数组常用操作 -----
// 获取长度
let count: number = dynastyNames.length; // 结果:6
// 访问元素(索引从0开始)
let firstDynasty: string = dynastyNames[0]; // 结果:'秦'
let lastDynasty: string = dynastyNames[5]; // 结果:'清'
// 添加元素到末尾
dynastyNames.push('元'); // 数组变成:['秦', '汉', '唐', '宋', '明', '清', '元']
// 删除末尾元素
let removed: string | undefined = dynastyNames.pop(); // 删除'元'
// ----- 3. 遍历数组 -----
// 方式一:for...of 循环(推荐,简洁)
for (let dynasty of dynastyNames) {
console.log(dynasty); // 依次输出:秦、汉、唐...
}
// 方式二:forEach 方法(需要索引时使用)
dynastyNames.forEach((dynasty: string, index: number) => {
console.log(`第${index + 1}个朝代:${dynasty}`);
});
⚠️ ArkTS 特殊规则:数组类型必须明确!
// ❌ 错误:ArkTS 不允许混合类型数组(无类型声明)
// let mixed = [1, 'hello', true];
// ✅ 正确:如果确实需要混合类型,使用联合类型
let mixedArray: (number | string)[] = [1, 'hello', 2, 'world'];
步骤 4:学习函数定义
// ============================================
// 函数定义
// ============================================
// ----- 1. 基本函数 -----
// function 函数名(参数: 类型): 返回类型 { ... }
function add(a: number, b: number): number {
return a + b;
}
// 调用函数
let sum: number = add(10, 20); // 结果:30
// ----- 2. 箭头函数(推荐) -----
// 更简洁的写法,常用于回调函数
const multiply = (a: number, b: number): number => {
return a * b;
};
// 单行箭头函数可以省略 {} 和 return
const square = (x: number): number => x * x;
// ----- 3. 带默认参数的函数 -----
function greet(name: string, greeting: string = '你好'): string {
return `${greeting},${name}!`;
}
let msg1: string = greet('张三'); // 结果:"你好,张三!"
let msg2: string = greet('李四', '早上好'); // 结果:"早上好,李四!"
// ----- 4. 可选参数 -----
// 参数名后加 ? 表示可选
function createUser(name: string, age?: number): string {
if (age !== undefined) {
return `${name},${age}岁`;
}
return name;
}
// ----- 5. 无返回值的函数 -----
// 返回类型用 void 表示
function logMessage(message: string): void {
console.log(`[LOG] ${message}`);
}
函数语法速查:
| 语法 | 说明 | 示例 |
|---|---|---|
function name() |
普通函数 | function add(a, b) |
const name = () => {} |
箭头函数 | const add = (a, b) => a + b |
param = value |
默认参数 | greeting = '你好' |
param? |
可选参数 | age?: number |
: void |
无返回值 | function log(): void |
---
第三部分:接口与枚举
步骤 5:创建官职接口(Position)
操作:在 lesson03/models 目录下创建 Position.ets 文件
这是「古今职鉴」项目的核心数据模型,用于描述古代官职的信息。
// ============================================
// 文件:lesson03/models/Position.ets
// 功能:定义官职数据模型
// ============================================
/**
* 官职信息接口
*
* 为什么用 interface?
* 1. 明确数据结构:告诉其他开发者这个对象长什么样
* 2. 类型检查:编译器会检查对象是否符合接口定义
* 3. 代码提示:编辑器会根据接口提供属性提示
*/
export interface Position {
// 必填属性
id: number; // 唯一标识,用于区分不同官职
name: string; // 官职名称,如"丞相"、"太尉"
dynasty: string; // 所属朝代,如"秦"、"汉"
rank: number; // 品级,1-9品,1品最高
category: string; // 类别:"文官" 或 "武官"
description: string; // 职责描述
// 可选属性(属性名后加 ?)
modernEquivalent?: string; // 现代对应职位,可能没有对应
salary?: string; // 俸禄,部分官职资料不全
}
/**
* 创建官职对象的工厂函数
*
* 为什么用工厂函数?
* 1. 简化创建过程:不用每次都写完整的对象结构
* 2. 参数校验:可以在函数内添加验证逻辑
* 3. 默认值处理:可以为可选参数提供默认值
*/
export function createPosition(
id: number,
name: string,
dynasty: string,
rank: number,
category: string,
description: string
): Position {
return {
id: id,
name: name,
dynasty: dynasty,
rank: rank,
category: category,
description: description
};
}
代码解释:
| 代码 | 含义 |
|---|---|
export interface Position |
导出接口,其他文件可以导入使用 |
id: number |
必填属性,类型是数字 |
modernEquivalent?: string |
可选属性,创建对象时可以不填 |
export function createPosition |
导出工厂函数 |
步骤 6:创建朝代枚举(Dynasty)
操作:在 lesson03/models 目录下创建 Dynasty.ets 文件
// ============================================
// 文件:lesson03/models/Dynasty.ets
// 功能:定义朝代枚举和相关工具函数
// ============================================
/**
* 朝代枚举
*
* 为什么用 enum 而不是字符串?
* 1. 防止拼写错误:Dynasty.QIN 不会写错,'秦' 可能写成 '奏'
* 2. 代码提示:输入 Dynasty. 会自动提示所有选项
* 3. 重构方便:如果要改名,只需改一处
*/
export enum Dynasty {
QIN = '秦', // 秦朝 (公元前221年 - 公元前207年)
HAN = '汉', // 汉朝 (公元前202年 - 公元220年)
WEI_JIN = '魏晋', // 魏晋南北朝 (公元220年 - 公元589年)
TANG = '唐', // 唐朝 (公元618年 - 公元907年)
SONG = '宋', // 宋朝 (公元960年 - 公元1279年)
YUAN = '元', // 元朝 (公元1271年 - 公元1368年)
MING = '明', // 明朝 (公元1368年 - 公元1644年)
QING = '清' // 清朝 (公元1644年 - 公元1912年)
}
/**
* 朝代详细信息接口
*/
export interface DynastyInfo {
id: Dynasty; // 朝代枚举值
name: string; // 朝代全称,如"秦朝"
startYear: number; // 起始年份(负数表示公元前)
endYear: number; // 结束年份
capital: string; // 都城
description: string; // 简介
}
/**
* 获取朝代主题色
*
* 为什么每个朝代有不同颜色?
* - 提升用户体验,让界面更有辨识度
* - 颜色参考了各朝代的历史文化特征
*/
export function getDynastyColor(dynasty: Dynasty): string {
switch (dynasty) {
case Dynasty.QIN:
return '#1a1a1a'; // 黑色 - 秦尚黑
case Dynasty.HAN:
return '#c41e3a'; // 红色 - 汉尚赤
case Dynasty.WEI_JIN:
return '#4a7c59'; // 青绿 - 魏晋风流
case Dynasty.TANG:
return '#ffd700'; // 金色 - 盛唐气象
case Dynasty.SONG:
return '#4169e1'; // 蓝色 - 宋代青花
case Dynasty.YUAN:
return '#228b22'; // 绿色 - 草原民族
case Dynasty.MING:
return '#8b0000'; // 深红 - 明代官服
case Dynasty.QING:
return '#4b0082'; // 紫色 - 清代皇家
default:
return '#333333'; // 默认灰色
}
}
/**
* 获取所有朝代信息列表
*/
export function getDynastyList(): DynastyInfo[] {
return [
{
id: Dynasty.QIN,
name: '秦朝',
startYear: -221,
endYear: -207,
capital: '咸阳',
description: '中国历史上第一个统一的中央集权制国家'
},
{
id: Dynasty.HAN,
name: '汉朝',
startYear: -202,
endYear: 220,
capital: '长安/洛阳',
description: '中国历史上继秦朝之后的大一统王朝'
},
{
id: Dynasty.TANG,
name: '唐朝',
startYear: 618,
endYear: 907,
capital: '长安',
description: '中国历史上最强盛的朝代之一'
}
];
}
步骤 7:创建历史人物接口
操作:在 lesson03/models 目录下创建 HistoricalFigure.ets 文件
// ============================================
// 文件:lesson03/models/HistoricalFigure.ets
// 功能:定义历史人物数据模型
// ============================================
import { Dynasty } from './Dynasty';
/**
* 历史人物接口
*/
export interface HistoricalFigure {
id: number;
name: string; // 姓名
dynasty: Dynasty; // 朝代(使用枚举)
positions: string[]; // 担任过的官职
achievements: string[]; // 主要成就
biography: string; // 人物简介
}
/**
* 获取示例历史人物数据
*/
export function getSampleFigures(): HistoricalFigure[] {
return [
{
id: 1,
name: '李斯',
dynasty: Dynasty.QIN,
positions: ['丞相', '廷尉'],
achievements: ['统一文字', '制定法律', '推行郡县制'],
biography: '秦朝著名政治家,辅佐秦始皇统一六国'
},
{
id: 2,
name: '萧何',
dynasty: Dynasty.HAN,
positions: ['丞相'],
achievements: ['月下追韩信', '制定汉律', '稳定后方'],
biography: '西汉开国功臣,被誉为"汉初三杰"之一'
},
{
id: 3,
name: '魏征',
dynasty: Dynasty.TANG,
positions: ['尚书左丞', '侍中'],
achievements: ['直言进谏', '贞观之治'],
biography: '唐朝著名谏臣,以直言敢谏著称'
}
];
}
---
第四部分:ArkTS 特殊规则(重要!)
ArkTS 为了性能优化,禁止了一些 TypeScript 的动态特性。这些规则必须牢记,否则编译会报错!
规则 1:❌ 禁止使用 any 类型
// ❌ 错误写法 - ArkTS 不允许 any
let data: any = { name: '张三' };
// ✅ 正确写法 - 定义具体的接口
interface UserData {
name: string;
}
let data: UserData = { name: '张三' };
为什么禁止 any?
- any 会绕过类型检查,失去类型安全
- 编译器无法优化 any 类型的代码
规则 2:❌ 对象字面量必须有类型
// ❌ 错误写法 - 对象没有类型声明
let config = {
theme: 'dark',
fontSize: 16
};
// ✅ 正确写法 - 先定义接口,再创建对象
interface AppConfig {
theme: string;
fontSize: number;
}
let config: AppConfig = {
theme: 'dark',
fontSize: 16
};
规则 3:❌ 禁止动态属性访问
interface User {
name: string;
age: number;
}
let user: User = { name: '张三', age: 25 };
// ❌ 错误写法 - 动态属性访问
let key: string = 'name';
let value = user[key]; // 编译错误!
// ✅ 正确写法 - 直接访问属性
let value: string = user.name;
规则 4:❌ 禁止索引签名
// ❌ 错误写法 - 索引签名
interface Dictionary {
[key: string]: string; // ArkTS 不支持!
}
// ✅ 正确写法 - 使用 Map
let dictionary: Map<string, string> = new Map();
dictionary.set('hello', '你好');
dictionary.set('world', '世界');
// 获取值
let value: string | undefined = dictionary.get('hello');
规则 5:❌ @Builder 中不能使用 const/let
// ❌ 错误写法 - @Builder 中声明变量
@Builder
MyBuilder() {
const title = '标题'; // 编译错误!
Text(title)
}
// ✅ 正确写法 - 直接使用表达式或调用方法
@Builder
MyBuilder() {
Text(this.getTitle()) // 调用方法获取值
}
getTitle(): string {
return '标题';
}
---
第五部分:综合实战
步骤 8:验证代码文件
确保你已经创建了以下文件:
products/jiaocheng/src/main/ets/lesson03/
└── models/
├── Position.ets # 官职接口
├── Dynasty.ets # 朝代枚举
└── HistoricalFigure.ets # 历史人物接口
步骤 9:运行构建验证
操作:在终端中运行构建命令
hvigorw assembleHap --no-daemon
预期结果:构建成功,无错误信息。
如果出现错误,请检查:
- 文件路径是否正确
- 语法是否有拼写错误
- 导入导出语句是否正确
---
本课小结
你学到了什么
| 知识点 | 说明 |
|---|---|
| 变量声明 | let(可变)、const(常量) |
| 基本类型 | string、number、boolean |
| 数组 | string[] 或 Array<string> |
| 函数 | 普通函数、箭头函数、默认参数、可选参数 |
| 接口 | interface 定义对象结构 |
| 枚举 | enum 定义固定常量集合 |
| ArkTS 限制 | 禁止 any、必须类型声明、禁止动态访问 |
创建的文件
products/jiaocheng/src/main/ets/lesson03/
└── models/
├── Position.ets # 官职接口和工厂函数
├── Dynasty.ets # 朝代枚举和工具函数
└── HistoricalFigure.ets # 历史人物接口
ArkTS 规则速查
| 规则 | TypeScript | ArkTS |
|---|---|---|
| any 类型 | ✅ 允许 | ❌ 禁止 |
| 无类型对象 | ✅ 允许 | ❌ 禁止 |
动态属性 obj[key] |
✅ 允许 | ❌ 禁止 |
| 索引签名 | ✅ 允许 | ❌ 禁止,用 Map |
| @Builder 中 const/let | - | ❌ 禁止 |
---
课后练习
练习 1:添加更多官职数据
在 Position.ets 中,使用 createPosition 函数创建以下官职:
- 大司马(汉,1品,武官)
- 太常(汉,3品,文官)
- 郎中令(汉,4品,文官)
练习 2:扩展朝代信息
在 Dynasty.ets 中,为 getDynastyList 函数添加更多朝代信息:
- 魏晋南北朝
- 宋朝
- 元朝
- 明朝
- 清朝
练习 3:创建官职分类枚举
创建一个新文件 PositionCategory.ets,定义官职分类枚举:
export enum PositionCategory {
CIVIL = '文官',
MILITARY = '武官',
IMPERIAL = '内廷',
LOCAL = '地方'
}
---
下一篇预告
[第4篇 ArkUI 核心概念:声明式 UI 入门](./04-ArkUI核心概念.md)
下一篇我们将学习:
- 声明式 UI 的核心思想
- @Component 和 @Entry 装饰器
- Text、Image、Button 等基础组件
- Column、Row、Stack 等布局容器
- 构建「古今职鉴」首页框架
---
参考资料
---
*本教程基于「古今职鉴」真实项目编写,所有代码均经过实际验证。*
项目开源地址
更多推荐


所有评论(0)