【HarmonyOS 6.1 全场景实战】《灵犀厨房》实战(三):ArkTS 高效开发:TypeScript 核心与 API 23 新规
摘要:HarmonyOS 6.1.0 API 23 强化了 ArkTS 的类型安全特性,彻底禁用 any 类型并引入严格类型检查。文章通过《灵犀厨房》案例,详解 ArkTS 如何继承 TypeScript 核心功能(接口、枚举、泛型)并优化为鸿蒙原生开发语言,同时介绍 API 23 新规如动态特性禁用、装饰器严格规则等。实战部分展示如何用类型安全重构项目,包括定义数据模型、应用枚举替代魔法值,最终
HarmonyOS 6.1 开发者盛宴|《灵犀厨房》实战(三):ArkTS 高效开发:TypeScript 核心与 API 23 新规
摘要:Stage 模型为我们搭好了“全场景”的骨架,但要在这副骨架上长出血肉,离不开 ArkTS 这门鸿蒙原生语言。它脱胎于 TypeScript,却又被 HarmonyOS 6.1.0 API 23 赋予了更严格的“安全基因”。今天,我们就用《灵犀厨房》的真实代码,把 ArkTS 的类型系统、装饰器和 API 23 新规一次性讲透——不但写得爽,还写得安全。
一、为什么 ArkTS 是“高效开发”的天花板?
很多从 JavaScript / TypeScript 转过来的开发者,一开始可能会觉得 ArkTS 只是“又一套 DSL”。但真正上手后,你会发现:ArkTS 在保留 TypeScript 灵活性的同时,把“类型安全”和“声明式 UI”焊死在了底层。
在 HarmonyOS 6.1.0 API 23 下,ArkTS 的设计哲学更加清晰:
- 严格的类型检查:
any已被彻底禁用,所有变量必须显式或可推导的类型,从源头掐死运行时类型错误。 - 声明式 UI:用
@State、@Prop、@Link等装饰器驱动视图,告别命令式操作 DOM 的繁琐。 - 专为全场景优化:组件、布局、状态管理都与 Stage 模型的无缝协作,让一次开发多端部署成为可能。
对我们《灵犀厨房》来说,这意味着:你可以用一份带类型的代码,同时约束手机、平板、智慧屏上的 UI 行为,任何数据流转都有“合同”可依。
一句话总结:ArkTS 不是“换皮 TypeScript”,而是 HarmonyOS 6.1.0 为“高可靠、高性能全场景应用”量身打造的装甲车。
二、TypeScript 核心在 ArkTS 中的“鸿蒙化”表达
ArkTS 继承了 TypeScript 绝大部分精华,但为了适应方舟运行时的要求,做出了很多针对性的强化。我们需要的,不是从头学一遍 TS,而是搞清楚 哪些能力可以直接用,哪些被“精装修”过。
2.1 基础类型的严格派
在 API 23 中,以下类型是你每天都会打交道的:
| TypeScript 类型 | ArkTS 行为 |
|---|---|
number |
64 位双精度浮点数,完全一致。 |
string |
不可变 UTF-16 字符串,一致。 |
boolean |
纯 true / false,一致。 |
array |
推荐 Array<T> 或 T[],一致。 |
object |
允许,但应优先使用接口/类代替。 |
any |
API 23 禁用。推荐用具体类型或 Object(当无可奈何时)。 |
unknown |
API 23 不推荐。用联合类型或泛型替代。 |
《灵犀厨房》立刻能用的改进:之前我们在 Index.ets 里写了一些 console.info 直接打印未知结构,现在就可以用明确的类型接口来约束数据。
2.2 接口与类:数据模型的“基因蓝图”
既然 any 被禁,用接口(interface)和类(class)定义数据结构,就成了最自然的选择。比如,后面我们要做的菜谱对象,可以这样定义:
// 定义菜谱的核心数据结构
export interface Recipe {
id: number;
name: string;
cover: Resource; // 图片资源用 Resource 类型
ingredients: string[];
steps: string[];
tags: string[];
calories: number; // 千卡
}
用接口的好处是,任何地方用到 Recipe 都会被 IDE 检查,如果少了字段或类型不对,编译期就会报红。在 API 23 下,这几乎就是强制要求了。
类(class)同样强大,而且可以配合 @Observed 装饰器实现深层状态观察。我们后面会在“购物清单”章节再详细展开。
2.3 枚举:让魔法数字滚出《灵犀厨房》
上一篇文章中,我们用字符串 'sm'、'md'、'lg' 表示断点,这种方式其实不够安全——万一拼错,布局就崩了。ArkTS 支持数字枚举和字符串枚举,正好用它来规范断点。
// 定义断点枚举
enum Breakpoint {
SM = 'sm',
MD = 'md',
LG = 'lg'
}
// 使用枚举替代魔法字符串
private getBreakpoint(width: number): Breakpoint {
if (width >= 840) return Breakpoint.LG;
if (width >= 600) return Breakpoint.MD;
return Breakpoint.SM;
}
这样一来,任何接受断点的函数都可以直接声明为 bp: Breakpoint,拼写错误直接编译报错。
2.4 泛型:让函数活起来,但不乱来
从 API 23 起,ArkTS 支持泛型,这让工具方法可以保持优雅的类型推导。例如封装一个响应式数组操作的辅助函数:
function addItem<T>(arr: T[], item: T): T[] {
return [...arr, item];
}
let tags: string[] = ['快手菜', '低脂'];
tags = addItem(tags, '下饭'); // 类型自动推断为 string[]
虽然《灵犀厨房》目前阶段还用不到太复杂的泛型,但提前建立意识,能让你后续封装能力时如虎添翼。
三、ArkTS 语言新规(API 23):红线与糖,一次讲明白
每到新 API 版本,HarmonyOS 都会给 ArkTS “收紧”一些安全带。踩准这些规则,不仅能通过编译,还能写出更健壮的代码。
3.1 动态特性全部禁用
eval、new Function、动态 import 等 JavaScript 传统艺能,在 ArkTS 中完全消失。方舟编译器需要在编译时就确定所有代码结构,这些运行时动态执行的能力自然不可能存在。
对我们来说,这意味着:所有逻辑都必须是静态可分析的。遇到需要动态派发逻辑时,可以用 switch...case 或映射表(Map/Record)代替,没有例外。
3.2 顶级声明必须是类或组件
在 ArkTS 中,每个 .ets 文件都必须是一个声明文件,不允许有孤立在类外的执行代码(除了一些编译期常量)。例如,你不能在文件根作用域写一个 console.log('Hello'),但定义常量 const PI = 3.14 是可以的。
我们的《灵犀厨房》页面都包裹在 @Component 结构体或 UIAbility 类中,天然满足这一要求。
3.3 类型断言的受限与推荐做法
TypeScript 常用的 as 语法在 ArkTS 中受到限制:只能用于某些安全场景,例如 (value as string).length。不允许使用 as any 绕过类型检查,因为 any 本身就不存在了。
当确实需要临时“宽泛类型”时,可以用 Object 过渡,然后迅速收窄:
function handleUnknown(input: Object): string {
// 先当成 object 访问,然后使用 typeof 保护
if (typeof (input as Record<string, Object>)?.name === 'string') {
return (input as Record<string, Object>).name as string;
}
return 'Unknown';
}
不过,最好的策略还是从一开始就用明确的联合类型或接口。
3.4 @State、@Prop、@Link 的严格规则
ArkUI 的状态管理装饰器在 API 23 下基本延续之前规则,但编译器检查更苛刻:
@State修饰的变量必须是简单类型、类或数组,不能是any,必须显式初始化。@Prop是从父组件单向同步的数据,子组件不能修改它。@Link是双向同步,但要求父组件对应字段必须是@State或@Provide等可观察源。
这些规则让我们在《灵犀厨房》中传递页面状态时有了清晰的数据流,避免“谁改了谁不知道”的混乱。
3.5 新增的实用语法糖(API 23)
在严控安全的同时,API 23 也带来了一点甜头:
- 可选链
?.完全支持:安全访问深层属性,不再需要层层if判空。 - 空值合并
??支持:非常适合给状态设默认值。 - 展开运算符
...:在数组和对象字面量中可用,但动态 key 扩展有限制,建议用在已知结构上。
例如《灵犀厨房》中设置默认标语:
@State slogan: string = '';
// ...
Text(this.slogan ?? '你的私人厨艺助手')
四、实战:用 ArkTS 新规重构《灵犀厨房》首页
光说不练假把式,我们来把上一章的 Index.ets 彻底改造一番,让它既能展示 ArkTS 最佳实践,又能给后续章节打好地基。
4.1 定义数据模型与枚举
在 entry/src/main/ets 下新建 model 文件夹,并创建 Recipe.ts(即使首页暂时不展示菜谱,也可以先定义):
// model/Recipe.ts
export interface Recipe {
id: number;
name: string;
cover: Resource;
ingredients: string[];
steps: string[];
tags: string[];
calories: number;
}
同时,在 Index.ets 同一目录下创建 Breakpoint.ts:
// Breakpoint.ts
export enum Breakpoint {
SM = 'sm',
MD = 'md',
LG = 'lg'
}
代码修改后的项目结构:
4.2 用类型武装首页
现在我们改写 Index.ets,全面应用类型安全:
import { window, display } from '@kit.ArkUI';
import { Recipe } from '../model/Recipe';
import { Breakpoint } from './Breakpoint';
@Entry
@Component
struct Index {
@State appName: string = '灵犀厨房';
@State slogan: string = '你的AI私人厨艺助手';
@State currentBreakpoint: Breakpoint = Breakpoint.SM;
private windowClass: window.Window | null = null;
private sizeChangeCallback: ((size: window.Size) => void) | null = null;
private density: number = 1; // 屏幕密度,默认为1
// 使用 vp 宽度判断断点
private getBreakpoint(pxWidth: number): Breakpoint {
const vpWidth = pxWidth / this.density;
if (vpWidth >= 840) return Breakpoint.LG;
if (vpWidth >= 600) return Breakpoint.MD;
return Breakpoint.SM;
}
async aboutToAppear(): Promise<void> {
try {
// 1. 获取屏幕密度,用于 px → vp 转换
const defaultDisplay = display.getDefaultDisplaySync();
this.density = defaultDisplay.densityPixels;
// 2. 获取窗口实例并读取初始尺寸
this.windowClass = await window.getLastWindow(getContext(this));
if (this.windowClass) {
const rect: window.Rect = this.windowClass.getWindowProperties().windowRect;
console.info(`初始窗口宽度: ${rect.width}px, 高度: ${rect.height}px, 密度: ${this.density}`);
this.currentBreakpoint = this.getBreakpoint(rect.width);
console.info(`初始断点: ${this.currentBreakpoint}`);
// 3. 注册窗口尺寸变化监听
this.sizeChangeCallback = (size: window.Size): void => {
const bp: Breakpoint = this.getBreakpoint(size.width);
this.currentBreakpoint = bp;
console.info(`断点切换为: ${bp} (pxWidth: ${size.width})`);
};
this.windowClass.on('windowSizeChange', this.sizeChangeCallback);
}
} catch (err) {
console.error('获取窗口实例失败', JSON.stringify(err));
}
}
aboutToDisappear(): void {
if (this.windowClass && this.sizeChangeCallback) {
this.windowClass.off('windowSizeChange', this.sizeChangeCallback);
}
}
build() {
Flex({
direction: this.currentBreakpoint === Breakpoint.SM ? FlexDirection.Column : FlexDirection.Row,
wrap: FlexWrap.NoWrap,
justifyContent: FlexAlign.Center,
alignItems: ItemAlign.Center
}) {
Column() {
Text('🍳')
.fontSize(this.currentBreakpoint === Breakpoint.SM ? 80 : 120)
Text(this.appName)
.fontSize(this.currentBreakpoint === Breakpoint.SM ? 36 : 48)
.fontWeight(FontWeight.Bold)
.fontColor('#FF6B35')
}
.margin({ bottom: this.currentBreakpoint === Breakpoint.SM ? 30 : 0 })
Column({ space: 15 }) {
Text(this.slogan ?? '你的私人厨艺助手')
.fontSize(18)
.fontColor('#999999')
Button('开始点菜')
.fontSize(18)
.backgroundColor('#FF6B35')
.borderRadius(24)
.padding({ left: 40, right: 40 })
}
.margin({ left: this.currentBreakpoint === Breakpoint.SM ? 0 : 40 })
}
.height('100%')
.width('100%')
.backgroundColor('#FFF8F0')
}
}
变化点解读:
- 引入
Breakpoint枚举,整个文件再无裸字符串'sm',想写错都难。Recipe接口定义了菜谱结构,虽还未使用,但为下一篇的智能推荐布局提前统一了数据口径。@State属性都有明确的类型和初始值,完全满足 API 23 的编译要求。console.info使用了模板字符串,可读性更好(这属于 ArkTS 保留的 ES6 特性)。- 所有变量都用
const声明(能不变的就不给机会变),符合最新规范。
4.3 验证
在 Phone 模拟器运行,并打开多屏模式拖拽变化窗口大小,Log 输出如下:
com.annan...ikitchen I 初始窗口宽度: 1280px, 高度: 2832px, 密度: 3.5
com.annan...ikitchen I 初始断点: sm
com.annan...ikitchen I Ability onBackground
com.annan...ikitchen I 断点切换为: sm (pxWidth: 1320)
com.annan...ikitchen I Ability onBackground
com.annan...ikitchen I Ability onForeground
com.annan...ikitchen I 断点切换为: sm (pxWidth: 1084)

日志解读:
初始窗口宽度: 1280px:这是windowRect.width返回的物理像素值(px),是系统最原始的数据,代码直接打印出来方便调试。密度: 3.5:该设备密度(densityPixels)为 3.5,意味着 1vp = 3.5px。- 断点判断过程(代码内暗箱操作)
打印完 px 后,内部立即执行了:
所以日志紧接着显示vpWidth = 1280 / 3.5 ≈ 366 → <600 → 断点 sm初始断点: sm。
同理,后续断点切换为: sm (pxWidth: 1320)也是 1320/3.5 ≈ 377 < 600,判断为 sm。
当前三块模拟屏的 vp 宽度均小于 600,断点统一显示sm,布局为纵向居中。
一切符合预期,并且代码层级比之前干净得多——这就是类型安全带来的“开发爽感”。
五、本阶段总结与下篇预告
今天,我们深度结合《灵犀厨房》,把 ArkTS 高效开发的三大支柱——TypeScript 核心、类型安全、API 23 新规——系统梳理并落地到了代码里。你学到了:
- 为什么 ArkTS 要禁掉
any:为全场景应用的可靠性上保险。 - 如何用接口、枚举、泛型建强类型模型:让数据在页面间传递时有“法律条文”可循。
- API 23 的关键变化:动态特性消失、类型断言受限、状态管理规则更严格。
- 实战重构:用新规把首页代码改造成了类型安全、可维护的现代 ArkTS 实现。
下篇预告:类型的地基已经铺好,接下来就进入《灵犀厨房》最让用户上瘾的功能——“今日吃什么”智能推荐布局(上)。我们将用一整套响应式网格和个性化排序,把“吃饭焦虑”彻底踩在脚下。
粉丝专属福利:为感谢大家支持,点赞 + 收藏 本专栏任意文章,并在评论区留言“纯血鸿蒙,我准备好了”,私信我即可领取《HarmonyOS 6.0 安全技术白皮书》电子版!
本文源码地址:
https://gitee.com/sulongannababy/lingxi-kitchen(第3章代码已同步更新)
如果你发现本文还有任何不严谨之处,欢迎随时指出,我们一起共建最优质的 HarmonyOS 6.1 学习内容!如果觉得有帮助,请不要吝啬你的点赞 👍、收藏 ⭐ 和评论 💬,我们下一篇见~
专栏完整大纲
说明:原第 2 篇《环境搭建与超级设备模拟器配置(API 23)》已合并至开篇词中,新加入第 39 篇《ArkUI 组件深度解析与自定义组件封装》,保证完整 40 篇体系。
| 序号 | 文章标题 |
|---|---|
| 1 | 开篇词:打造消除“吃饭焦虑”的《灵犀厨房》(含环境搭建) |
| 2 | Stage 模型项目结构与设备形态适配策略 |
| 3 | ArkTS 高效开发:TypeScript 核心与 API 23 新规(已完成) |
| 4 | 【首页开发】“今日吃什么”智能推荐布局(上) |
| 5 | 【首页开发】“今日吃什么”智能推荐布局(下) |
| 6 | 【食材识别】调用相机拍照与图像分析服务 |
| 7 | 【AI 推荐逻辑】基于偏好与食材生成推荐菜谱 |
| 8 | 【菜谱详情】沉浸式分步浏览页 |
| 9 | 状态管理与跨组件通信:实现食材勾选 |
| 10 | 【购物清单】一键生成并分组展示 |
| 11 | 【数据打通】访问 Health Kit 获取健康数据 |
| 12 | 【营养分析引擎】计算个性化卡路里建议 |
| 13 | 【智能厨电模拟】用代码模拟发现与控制设备 |
| 14 | 【分布式流转】手机选菜谱→平板看步骤→智慧屏播视频 |
| 15 | 超级设备模拟器实战:多设备交互调试技巧 |
| 16 | 【语音合成】烹饪步骤分步语音播报 |
| 17 | 【语音识别】实现“下一步”“重复”等声控操作 |
| 18 | 【手表协同】烹饪计时器流转至智能手表 |
| 19 | 通知系统:多设备推送提醒 |
| 20 | 【元服务】一键烹饪推荐原子化服务 |
| 21 | 【服务卡片】在桌面查看烹饪进度 |
| 22 | 多媒体:AVPlayer 嵌入教学视频 |
| 23 | 交互动效:转场、列表动画与趣味反馈 |
| 24 | 手势操作:滑动调整“火力大小” |
| 25 | 深色模式与多主题开发 |
| 26 | 响应式布局:折叠屏与平板完美适配 |
| 27 | 并发优化:TaskPool 加速图片分析 |
| 28 | 【收藏与历史】使用 Relational Store 持久化数据 |
| 29 | 【个人中心】偏好设置与过敏源管理 |
| 30 | 【社区分享】用户菜谱分享与展示 |
| 31 | 应用权限管理与隐私保护最佳实践 |
| 32 | 性能调优:使用 DevEco Profiler 提升流畅度 |
| 33 | 自动化测试与单元测试编写 |
| 34 | 签名与证书管理:真机调试准备 |
| 35 | 打包与发布:生成 Release 包与混淆 |
| 36 | 上架 AppGallery Connect 全程指南 |
| 37 | 数据分析:集成分析服务查看用户行为 |
| 38 | ArkUI 组件深度解析与自定义组件封装(新增) |
| 39 | 社区推广:如何利用 CSDN 等平台推广你的鸿蒙应用 |
| 40 | 专栏总结:回顾 40 篇,开启你的 HarmonyOS 生态新征程 |
更多推荐



所有评论(0)