首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >ArkTS 不是 TypeScript:把 TS 代码粘进 DevEco,编译器到底拦你什么

ArkTS 不是 TypeScript:把 TS 代码粘进 DevEco,编译器到底拦你什么

原创
作者头像
权哥说IT
发布于 2026-11-09 17:57:38
发布于 2026-11-09 17:57:38
90
举报

导语:网上搜到一段 TypeScript,粘进 DevEco Studio 就一片红。很多人第一反应是「环境配错了」「版本不对」,其实是 ArkTS 主动砍掉了一批 TS 写法。这篇文章把最常撞的六条拦法逐条拆开——每条先说 TS 里为什么能过,再说 ArkTS 为什么必须拦,最后给能直接跑的改法。读完你能判断:手上那段代码是该改写法,还是根本不该带到鸿蒙项目里。

写鸿蒙应用的第一个坎,不是 ArkUI 怎么布局,也不是 Stage 模型怎么起页面。

是你原来写 TypeScript 的手感,在这儿突然不管用了。

any 报错,var 报错,给对象补个字段报错,两个字段长得一模一样的类互相赋值也报错。报错信息还很整齐,全是 arkts- 开头的一串编号。

这些不是 bug,但也别急着记成「ArkTS 比 TS 更严」这么一句就算完。

准确的关系是这样的:ArkTS 基于 TypeScript,往上加了一套 ArkUI 的声明式语法,往下砍掉了一批 TS 的写法。

加的那部分(@Entry、@Component、struct、build()),标准 TypeScript 编译器不认识。砍的那部分,ArkTS 编译器不放过。

所以它既不是纯粹的 TS 超集,也不是纯粹的 TS 子集——两个方向都动过。

华为官方那句原话是「ArkTS is a superset of TypeScript」,因为声明式 UI 那块确实是 TS 没有的。

同一篇文档里还写了一句「restricts the features of TypeScript」,禁掉的是一批会和静态类型冲突的动态写法。

两句话都得看。只看前一句,会以为 TS 代码能直接跑;只看后一句,会以为它只是 TS 的阉割版。

对我们的实际影响只有一条:从 TS 带过来的代码,过了 TypeScript 编译器不等于过得了 ArkTS;反过来,ArkTS 里带装饰器的那些写法,粘回 TS 项目里也会一片红。

砍掉的部分换了什么,值得先说清楚。

一、它为什么要砍:编译期能定死的事,就别留到运行时

JavaScript 是动态语言,对象的形状可以在运行时变。V8 这类引擎为了跑得快,会在内部偷偷给每个对象记一套「隐藏类」,把「这个对象有哪些字段、每个字段在第几个偏移」缓存下来。这套优化是引擎尽力而为的,一旦你动态加字段,缓存作废,要重新算。

ArkTS 把这件事从「引擎尽力优化」变成了「语言层面保证」:

  • 对象布局在编译期就固定,运行时不允许增删字段;
  • 类型在编译期强制确定,运行时不会再变;
  • 编译目标不是 JavaScript,而是方舟字节码(.abc),运行时同时支持解释器、AOT 和 JIT。

布局定死之后,访问一个属性不用再走哈希查找,直接按偏移量取。这是鸿蒙敢把 ArkTS 用在系统级应用上的底气之一。

代价就是你写的时候得多守几条规矩。

规矩不是找茬,是把「运行时才炸的雷」提前到「写的时候就告诉你」。

ArkTS 与 TypeScript 的关系:往上一头加了 ArkUI 声明式语法,往下一头砍掉了动态类型的自由,两个方向都动过
ArkTS 与 TypeScript 的关系:往上一头加了 ArkUI 声明式语法,往下一头砍掉了动态类型的自由,两个方向都动过

ArkTS 与 TypeScript 的关系:左边是 TS 没有、它加上去的,右边是 TS 有、它砍掉的。两个方向都动过,所以「超集」和「做了限制」两个说法各对一半。

这张图值得记一下。左边那批是 TS 没有的,右边那批是 TS 有、ArkTS 不要的。 后面六条报错全落在右边那一栏里——它们不是 ArkTS 新立的规矩,是 TS 里合法、到了这儿被拿掉的东西。

下面六条,按我实际撞到的频率排序。

二、var 直接不让用

TS 里 var 只是不推荐,不是不能写。ArkTS 里它是编译错误。

代码语言:javascript
复制
var count = 0;        // 报错:arkts-no-var
let count = 0;        // 对
const MAX = 100;      // 对

理由不难理解:var 的作用域会穿透块,在循环和回调里最容易埋雷。let 和 const 是块级作用域,行为可预测。

变量声明方式的变化,看着是语法差异,实际是把一整类作用域事故提前删掉了。

改法只有一条:能不改就用 const,要改就用 let,永远别用 var。

顺带一个差别:let 在同一作用域里不能重复声明,var 可以。写循环变量时留意一下,TS 里习惯性写两遍同名 var 的代码,搬过来第一个就红。

三、any 和 unknown 被禁了

这条是 TS 老手最容易栽的。遇到「不知道是什么类型」,TS 的第一反应是写 any 糊过去,ArkTS 直接把这条路堵死。

代码语言:javascript
复制
let data: any = getData();      // 报错:arkts-no-any
let info: unknown = res;        // 也报错

它的态度很明确:不知道类型就去查清楚,不许用「随便」应付。

那接口返回的数据怎么办?定义一个 interface 或 class 把形状写清楚,再做类型断言:

代码语言:javascript
复制
interface User {
  name: string;
  age: number;
}

let u = res as User;

华为那篇《从 TypeScript 到 ArkTS 的适配规则》里给过一个数字:any 在 TypeScript 代码库里的使用率大约 1%。

口径要看清楚,原文写的是「只有大约 1% 的 TypeScript 代码库使用」——按代码库算,不是按代码行算。 意思是绝大多数项目里 any 只是零星出现,不是说 1% 的行写了 any。

禁掉它的理由是性能:编译器不用在运行时反复做类型检查。官方把这笔账算成「重构量小、收益明显」。

这 1% 恰恰是事故高发的那 1%。

any 不是「我不知道类型」,是「我不想现在想清楚」。这笔账会在三个月后连本带利地还。

四、对象不能动态长出新属性

这条是从 JS/TS 转过来撞得最狠的。

代码语言:javascript
复制
let obj = { name: '张三' };
obj.age = 18;           // 报错:对象布局不能改
delete obj.name;        // 报错:arkts-no-delete

TS 里这么写很常见,严格模式下会提示,但 as any 一绕就过去了。ArkTS 里绕不过去。

正确的做法是把字段一次写全:

代码语言:javascript
复制
class Person {
  name: string = '';
  age: number = 0;
}

let p = new Person();
p.age = 18;             // 对,属性本来就存在

想加字段?回头改 class 的定义,别在运行时硬塞。

这条规矩还有个连带影响:TS 项目里爱用 Record 或者索引签名 [k: string]: any 存动态配置,在 ArkTS 里都不行。换成 Map,或者把字段明确写出来。

我一开始不信这个邪,觉得「配置表就是动态的,怎么可能定死」。后来发现改成 Map 之后,取值的合法性反而被编译器兜住了——原来那些「配置里少写一个字段,运行时才 undefined is not a function」的问题,编译期就没了。

五、结构一样也不认,它只认名字

这条最反直觉,也最容易让人以为编译器坏了。

代码语言:javascript
复制
class A {
  x: number = 0;
}

class B {
  x: number = 0;
}

let a: A = new B();     // 报错!两个类字段一模一样

TypeScript 用的是结构化类型(structural typing):两个类型只要长得一样,就算没有继承关系也能互相赋值。

ArkTS 用的是名义类型(nominal typing):类型之间的关系必须显式声明,靠 implements 接口或者继承建立。

代码语言:javascript
复制
interface HasX {
  x: number;
}

class A implements HasX {
  x: number = 0;
}

class B implements HasX {
  x: number = 0;
}

let a: HasX = new A();  // 对,显式实现了接口

为什么必须这么严?因为两个字段相同的类型,语义上可能毫无关系。

把「用户 ID」赋给「订单 ID」,在结构化类型下编译器是放行的——它俩都是 number。这类错误在业务代码里极难发现,等到对账对不上才知道。

结构化类型省的是写代码的手,名义类型省的是查 bug 的时间。

六、null 不能随便塞进去

空安全是另一条高压线。类型后面不加问号,就代表这个变量绝不为空。

代码语言:javascript
复制
let a: string = null;              // 报错:string 不含 null
let b: string | null = null;       // 对,明确声明可空
let c: string | undefined;         // 对,但要自己处理 undefined

三个变量分开写是有意的——上面第二节刚说过 let 在同一作用域不能重复声明,三行都叫 name 的话,编辑器会先报一个重名错误,把真正要讲的空安全错误盖掉。

用可空类型之前必须先判断:

代码语言:javascript
复制
if (this.user != null) {
  console.info(this.user.name);
}

// 或者用空值合并
let n = this.user?.name ?? '未登录';

一句话记:别人给你什么类型,你就还什么类型,别拿 null 糊弄。

空安全的本质不是多写一个问号,是强迫你在写的时候就想清楚「这个值有没有可能没有」。

前五条各占一节,第六条(索引签名)在第四节末尾顺带交代过了。现在给一张全景——左边三条是我最先撞上的,右边三条是后来才慢慢习惯的:

六条拦截一览:var、any、动态加字段、结构性赋值、null 空值、索引签名,每条都配了对应的 arkts- 报错编号和改法
六条拦截一览:var、any、动态加字段、结构性赋值、null 空值、索引签名,每条都配了对应的 arkts- 报错编号和改法

最常撞的六条,每条都对应一个 arkts- 报错编号。左边三条最先撞上,右边三条是后来才习惯的。

这六条其实可以压成一句话:ArkTS 要求你在写下每个变量、每个对象、每个字段的时候,就已经知道它是什么。说不上来的,它就不让你过。

七、还有几条顺手被砍的

除了上面六条,这几个写法在 ArkTS 里也见不到。我把它们列成一张表,方便你照着对照自己手上的代码:

TS 里能写

ArkTS

改成什么

eval()

禁止

没有替代,本来就不该用

with 语句

禁止

直接写完整对象访问

索引签名 [k: string]: any

禁止

Map 或明确字段

Function / Object 当类型

禁止

写具体函数签名或类名

function* 生成器

禁止

async / await

函数声明里的参数解构

禁止

显式传参

@ts-ignore / @ts-nocheck

禁止

老实声明类型

in 运算符查属性

禁止

instanceof

交叉类型 A & B

禁止

接口继承 extends

类型声明合并

禁止

保持声明封闭

报错编号就是说明书

看到 arkts- 开头的编号,别急着搜教程——直接去官方文档搜这个编号,说明和改法都是现成的。它会直接告诉你「哪个特性不支持」,比对着症状猜快得多。

几个高频的:

报错关键词

意思

怎么改

arkts-no-any

用了 any / unknown

换成具体类型

arkts-no-var

用了 var

改 let 或 const

arkts-no-untyped-obj-literal

对象字面量没类型

定义 class 或 interface

arkts-no-props-by-index

用下标取对象属性

改成点字段名

arkts-strict-typing

类型不匹配

查是不是结构类型互赋了

这张表我建议存成图片放在手边,被拦住的时候对一眼比搜教程快:

报错编号速查:八个高频 arkts- 编号,各配一句人话解释和改法
报错编号速查:八个高频 arkts- 编号,各配一句人话解释和改法

八个高频报错编号速查。对着编号查文档,比对着症状猜快得多。

八、这套规矩,学的时候得有人逐条讲

这些限制单看一条都不难,难的是TS 的手感是肌肉记忆。

你会下意识写 any,下意识给对象补字段,下意识靠结构相似去赋值。这些动作不需要思考,所以编译器报错的时候,第一反应往往是「我明明写对了」。

更麻烦的是,这些知识点在网上的资料里是散的。搜到一篇讲名义类型的,没讲索引签名;搜到一篇讲对象布局的,没讲它跟 Map 的关系。凑起来是一堆碎片,凑不成一套判断标准。

碎片化的知识能让你看懂一条报错,但撑不起「这段代码该不该这么写」的判断。

如果你不打算靠零散看文档慢慢磨,想系统学一遍,可以找提供鸿蒙全栈开发培训的机构来咨询。比如誉天教育的 AI 鸿蒙全栈课,课程纲要是从 ArkTS 与 Stage 模型、ArkUI 与业务页面、数据网络与权限到多端适配与 AI 入口这一路铺下来的,语言基础这块排在前面。

这个铺法合我意。语言差异这块没弄明白,后面写页面基本就是一直在跟编译器较劲。你要是本来就在挑课,可以拿这一条去对照:它有没有单独给 ArkTS 的语言差异留出位置。

直接跳进 ArkUI 布局的,讲的是「怎么把页面画出来」;先把「这套语言和 TS 差在哪」讲透的,讲的才是「怎么不让编译器天天拦你」。这两件事的难度不在一个量级。

九、回到标题

ArkTS 不是 TypeScript 加了个新名字,也不是它减了几条语法。

它是往两头各走了一步:往上一头加了 @Entry、@Component、struct 这套声明式语法,往下砍掉了那些「运行时才可能炸」的自由。砍掉的部分换来的是编译期确定性和运行效率——这笔交易划不划算,取决于你是在写一个 demo,还是在写一个要上架、要迭代、要在别人手机上跑三年的应用。

demo 里 any 一把梭最快。上架之后,那 1% 的 any 会变成半夜的崩溃日志。

你的第一段 ArkTS 代码,是被哪条 arkts- 报错拦下的? 评论区说说。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 一、它为什么要砍:编译期能定死的事,就别留到运行时
  • 二、var 直接不让用
  • 三、any 和 unknown 被禁了
  • 四、对象不能动态长出新属性
  • 五、结构一样也不认,它只认名字
  • 六、null 不能随便塞进去
  • 七、还有几条顺手被砍的
    • 报错编号就是说明书
  • 八、这套规矩,学的时候得有人逐条讲
  • 九、回到标题
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档