鸿蒙开发入门指南:从前端TypeScript到ArkTS过渡指南
鸿蒙开发入门指南:从前端TypeScript到ArkTS过渡指南
- 一、先理解最核心的一句话
- 二、为什么要从TS迁移到ArkTS?
- 三、迁移核心:从“能用”到“规范”
- 3.1 最需要改的10条规则(新手必看)
- 规则1:使用let,别用var(arkts-no-var)
- 规则2:别用any和unknown(arkts-no-any-unknown)
- 规则3:类的属性必须初始化(编译强制)
- 规则4:构造函数里不能声明字段(arkts-no-ctor-prop-decls)
- 规则5:用箭头函数,别用函数表达式(arkts-no-func-expressions)
- 规则6:用点访问属性,别用方括号(arkts-no-props-by-index)
- 规则7:类型转换用as,别用尖括号(arkts-as-casts)
- 规则8:别用解构赋值(arkts-no-destruct-assignment)
- 规则9:别用for...in,用普通for循环(arkts-no-for-in)
- 规则10:throw只能抛Error对象(arkts-limited-throw)
- 3.1 最需要改的10条规则(新手必看)
- 四、进阶迁移:这些特性ArkTS不支持
- 五、迁移实战:一个真实案例
- 六、兼容性说明:版本差异要注意
- 七、迁移清单:照着这个改就行
- 八、总结:一张表记住TS和ArkTS的核心差异
- 结语:写给正在迁移代码的你
大家好,我是木斯佳。
很多从前端转鸿蒙的朋友,第一次接触ArkTS时都会有这样的疑惑:

“这不就是TypeScript吗?为什么我的TS代码复制进去就报错?”
“ArkTS到底改了啥?我写了这么多年的TS,现在要重新学?”
别慌。今天这篇文章,我就用跨端开发者的视角,帮你彻底理清从TS到ArkTS的过渡容易坑的点。文章有点长,但保证你看完就能懂,懂了就能改。
一、先理解最核心的一句话
ArkTS = TypeScript - 动态特性 + 更严格的静态检查
就这么简单。
ArkTS保留了TS的大部分语法,但砍掉了一些影响性能和稳定性的动态特性,同时加强了编译时的类型检查。这么做的目的是什么?
- 程序更稳定:运行时少报错
- 性能更好:减少运行时的类型检查
- 代码更清晰:类型一目了然
说白了,就是用开发时的“麻烦”换运行时的“顺畅”。
二、为什么要从TS迁移到ArkTS?
一部分是像我这种前端开发,编程习惯导致很多时候肌肉记忆写出来的代码在鸿蒙编译不通过。
另外在工程化上,我们有很多web三方的utils库是非常经典的,在造轮子的过程中,不可避免的会从一个范式到另一个范式的迁移过渡。
2.1 程序稳定性:把错误扼杀在编译阶段
动态类型语言(比如JavaScript)虽然开发爽,但运行时容易翻车。最典型的就是undefined错误:
// TypeScript(非严格模式)classPerson{ name:string;// 没初始化,实际是undefinedgetName():string{returnthis.name;// 返回类型写string,但实际可能是undefined}}let buddy =newPerson(); buddy.getName().length;// 运行时崩溃:name is undefined这个问题在TS里可能被忽略,但在ArkTS里根本编译不过:
// ArkTSclassPerson{ name:string='';// 必须显式初始化getName():string{returnthis.name;// 现在保证是string,不会是undefined}}let buddy =newPerson(); buddy.getName().length;// 0,安全运行如果name真的可能为空,那就得明确写出来:
classPerson{ name?:string;// 可能为undefinedgetName():string|undefined{// 返回类型也要匹配returnthis.name;}} buddy.getName()?.length;// 必须用可选链,否则编译报错2.2 程序性能:减少运行时的类型检查
JavaScript引擎为了处理动态类型,需要在运行时做大量检查。比如这个简单的函数:
functionnotify(who:string, what:string){console.info(`Dear ${who}, a message for you: ${what}`);}在JS里,如果有人调用notify(null, undefined),引擎就得做类似这样的检查:
function__internal_tostring(s){if(typeof s ==='string')return s;if(s ===undefined)return'undefined';if(s ===null)return'null';// ...}ArkTS通过强制静态类型,保证运行时不会出现非string类型的值,这些检查就可以省略,性能自然就上去了。
三、迁移核心:从“能用”到“规范”
3.1 最需要改的10条规则(新手必看)
下面是我总结的、迁移中最常遇到的10条规则,看完就能上手改代码。
规则1:使用let,别用var(arkts-no-var)
错误:
var x =10;正确:
let x =10;规则2:别用any和unknown(arkts-no-any-unknown)
错误:
let value:any='hello'; value =42;// 随便改类型正确:
let value:string='hello';// 明确类型// 或者如果真的需要多种类型let value2:string|number='hello'; value2 =42;// 合法规则3:类的属性必须初始化(编译强制)
错误:
classPerson{ name:string;// 没初始化 age:number;// 没初始化}正确:
classPerson{ name:string=''; age:number=0;// 或者在构造函数里初始化constructor(name:string, age:number){this.name = name;this.age = age;}}规则4:构造函数里不能声明字段(arkts-no-ctor-prop-decls)
错误(TS的简写语法):
classPerson{constructor(private name:string,// 直接在参数里声明字段public age:number){}}正确:
classPerson{private name:string;public age:number;constructor(name:string, age:number){this.name = name;this.age = age;}}规则5:用箭头函数,别用函数表达式(arkts-no-func-expressions)
错误:
letf=function(s:string){console.info(s);}正确:
letf=(s:string)=>{console.info(s);}规则6:用点访问属性,别用方括号(arkts-no-props-by-index)
错误:
let obj ={ name:'John'};console.info(obj['name']);// 索引访问正确:
let obj ={ name:'John'};console.info(obj.name);// 点访问规则7:类型转换用as,别用尖括号(arkts-as-casts)
错误:
let value =<string>someVar;// TS风格正确:
let value = someVar asstring;// ArkTS风格规则8:别用解构赋值(arkts-no-destruct-assignment)
错误:
let[a, b]=[1,2];[a, b]=[b, a];// 交换变量正确:
let arr =[1,2];let a = arr[0];let b = arr[1];let tmp = a; a = b; b = tmp;规则9:别用for…in,用普通for循环(arkts-no-for-in)
错误:
let arr =['a','b','c'];for(let i in arr){console.info(arr[i]);}正确:
let arr =['a','b','c'];for(let i =0; i < arr.length; i++){console.info(arr[i]);}// 或者用for...of(支持)for(let item of arr){console.info(item);}规则10:throw只能抛Error对象(arkts-limited-throw)
错误:
throw'出错了';// 抛字符串throw404;// 抛数字正确:
thrownewError('出错了');四、进阶迁移:这些特性ArkTS不支持
4.1 不支持动态修改对象
在ArkTS里,对象的布局是固定的,不能在运行时添加或删除属性。
classPoint{ x:number=0; y:number=0;}let p =newPoint();// 以下操作都不允许delete p.x;// 编译错误 p.z =10;// 编译错误(p asany).z =10;// 编译错误(不能用any绕过)怎么改:在设计类的时候就把所有可能的属性都定义好。
4.2 不支持structural typing
在TS里,只要两个类结构一样,就可以互相赋值:
// TypeScriptclassT{ name:string='';greet(){console.info('Hello');}}classU{ name:string='';greet(){console.info('Greetings');}}let u:U=newT();// TS里合法,结构相同ArkTS不支持这个,必须通过继承或实现接口来建立关系:
// ArkTSinterfaceGreetable{ name:string;greet():void;}classTimplementsGreetable{ name:string='';greet(){console.info('Hello');}}classUimplementsGreetable{ name:string='';greet(){console.info('Greetings');}}let u: Greetable =newT();// 合法,都实现了相同接口4.3 不支持运算符重载
ArkTS对一元运算符有严格限制,只能用于数值类型:
let a =+5;// 合法let b =+'5';// 编译错误,不能把字符串转数值let c =-'5';// 编译错误// 需要显式转换let d =Number('5');// 正确做法4.4 不支持解构声明
// 错误let{x, y}= point;// 正确let x = point.x;let y = point.y;五、迁移实战:一个真实案例
假设我们有一个TS写的工具类,看看怎么迁移到ArkTS。
TS原版代码

// utils.tsclassLogger{ level:string;// 没初始化constructor(level?:string){if(level){this.level = level;}}log(message:string){console[this.level](message);// 可能报错}}// 使用let logger =newLogger(); logger.log('hello');// 运行时崩溃:this.level是undefinedArkTS迁移后

// utils.etsclassLogger{ level:string='log';// 给个默认值constructor(level?:string){if(level){this.level = level;}}log(message:string){// 安全访问if(this.level ){}else{console.log(message);}}}// 使用let logger =newLogger(); logger.log('hello');// 安全运行六、兼容性说明:版本差异要注意
6.1 根据SDK版本的不同
| compatibleSdkVersion | 模式 | 行为 |
|---|---|---|
| >= 10 | 标准模式 | 必须严格遵循ArkTS语法,否则编译失败 |
| < 10 | 兼容模式 | 违反规则只给警告,仍可编译成功 |
建议:新项目直接上标准模式,老项目逐步迁移。
6.2 文件引用规则
- ✅
.ets文件可以import.ets/.ts/.js文件 - ❌
.ts/.js文件不能import.ets文件
这意味着核心逻辑最好写在.ets文件里,避免循环依赖。
七、迁移清单:照着这个改就行
必改项(编译错误)
- 把
var改成let - 去掉所有
any和unknown - 类的属性必须初始化
- 构造函数参数里不能声明字段
- 函数表达式改成箭头函数
- 属性访问用点,别用方括号
- 类型转换用
as,别用尖括号 - 去掉解构赋值
for...in改成for循环或for...ofthrow只能抛Error对象
建议改(警告)
- 不要用
Function.bind/call/apply - 不要用
globalThis - 不要用
as const - 不要用
@ts-ignore(ArkTS里直接报错)
八、总结:一张表记住TS和ArkTS的核心差异
| 特性 | TypeScript | ArkTS | 迁移建议 |
|---|---|---|---|
| 类型系统 | 渐进式 | 强制静态 | 所有变量都要有明确类型 |
any类型 | 支持 | ❌ 不支持 | 用具体类型或联合类型替代 |
| 属性初始化 | 可选 | 必须 | 声明时就初始化或在构造函数里初始化 |
| 解构赋值 | 支持 | ❌ 不支持 | 拆开写 |
for...in | 支持 | ❌ 不支持 | 用for或for...of |
| 对象动态属性 | 支持 | ❌ 不支持 | 类里预先定义好所有属性 |
throw类型 | 任意 | 只能是Error | throw new Error() |
this使用 | 任意位置 | 仅限于实例方法 | 静态方法和函数里不能用this |
| 文件引用 | 任意 | .ets不能被子.ts引用 | 核心逻辑写在.ets |
结语:写给正在迁移代码的你
写完这篇迁移指南,我想和你聊几句心里话。
说实话,刚接触ArkTS的时候,我也觉得挺烦的——明明TS写得好好的,为什么要改?明明any多方便,为什么不让用?
但真正把代码迁移过来之后,我才理解了这个设计的良苦用心。
你发现没有?ArkTS砍掉的这些特性,恰恰是我们在TS里最容易踩坑的地方:
any一时爽,重构火葬场- 属性不初始化,运行时undefined报错
- 解构赋值看起来很酷,但类型推导经常出问题
- 动态添加属性,代码越写越乱
ArkTS的做法,相当于帮你把“可以偷懒但容易翻车”的路都堵上了。刚开始会觉得不习惯,但写久了你会发现:代码更稳了,bug更少了,团队协作也更顺畅了。
迁移的过程,其实就是从“能用”走向“规范”的过程。
刚开始可能会慢一点,但每改一个any,每初始化一个属性,你都在为自己的代码增加一分稳定性,为未来的自己减少一个debug的夜晚。
- 从新模块开始:别想着一次性迁移整个项目,新写的代码直接用ArkTS规范,老代码逐步改造
- 用好编译错误:ArkTS的编译器很严格,但它报的错都是为你好的。每修复一个错误,就少了一个潜在的运行时bug
- 保持学习心态:刚开始会觉得约束太多,但慢慢你会发现,这些约束其实是在帮你建立更好的编码习惯
- 遇到问题多思考:不要简单地“把报错改掉”,而是理解为什么报错,ArkTS想让你怎么写更好
技术这条路很长,从一个框架到另一个框架,从一种范式到另一种范式,每一次迁移都是一次成长。

我是木斯佳,希望这篇指南能帮你在鸿蒙的世界里少踩几个坑。下篇见。