《从 TypeScript 到 ArkTS 的适配规则》精简笔记
这一篇可以当成:"TS 项目迁到 ArkTS 时,哪些特性不能用 / 要怎么改" 的速查表 。
大原则:ArkTS = 强类型 + 固定对象布局 + 简化类型系统 + 编译期检查更严格。
0. 总体原则 & 心态
约束级别
- 错误(Error):必须改,不改就编译失败。
- 警告(Warning):现在还能过,将来可能变成错误;能改则尽量改。
不再鼓励的两大类特性
- 动态类型 & 动态对象布局
any / unknown、动态加属性、删属性、原型操作、for..in、in、delete等都被限制。
- 编译器负担重 / 容易写出"类型体操"的特性
- 条件类型、映射类型、泛型 infer、structural typing、大部分 utility types、JSX、UMD 等。
1. 强制静态类型:any / unknown 彻底禁用
1.1 禁止 any / unknown
ts
// ❌ ArkTS 不支持
let value1: any;
let value2: unknown;
要么写具体类型,要么用 Object:
// ✅ ArkTS
let valueB: boolean = true;
let valueN: number = 42;
let valueO1: Object = true;
let valueO2: Object = 42;
结论:见到 any / unknown → 一定要替换成具体类型 (或者顶多
Object)。
1.2 禁止关闭类型检查
- 不支持:
// @ts-ignore、// @ts-nocheck - 严格模式永远开启:
-
strictNullChecks -
strictPropertyInitialization -
noImplicitReturns -
strictFunctionTypes// ArkTS
let s1: string | null = null; // 合法
let s2: string = null; // ❌ 编译错误
-
2. 对象布局固定:不能动态改 Class 的"形状"
2.1 不能添加 / 删除属性(即使用 any)
class Point {
x: number = 0;
y: number = 0;
}
let p = new Point();
// ❌ 删除属性
delete (p as any).x;
// ❌ 运行时新增属性
(p as any).z = 'label';
ArkTS:类一旦定义完,实例的字段集合就固定,不能在运行时"变胖变瘦"。
2.2 禁止 Symbol() API(除 Symbol.iterator)
// ❌ ArkTS 不支持自定义 Symbol
let prop = Symbol();
(p as any)[prop] = 1;
3. 限制运算符 & 语法特性
3.1 一元运算符 + - ~ 只允许数值
let a = +5; // ✅
let b = +'5'; // ❌ ArkTS 编译错误
let c = -'5'; // ❌
let d = ~'5'; // ❌
以前 TS 里很多"隐式字符串转 number"写法,在 ArkTS 全都不行。
3.2 禁止 delete / in / for..in
// ❌ delete
class Point { x: number | null = 0; y: number | null = 0; }
let p = new Point();
p.y = null; // 用 null 表示"无值"
// ❌ in
let b = 'name' in p; // ArkTS 不支持
// ❌ for..in
let arr: string[] = ['1', '2'];
for (let i = 0; i < arr.length; ++i) {
console.info(arr[i]);
}
3.3 不支持解构:包含 解构赋值 / 解构声明 / 参数解构
// ❌ ArkTS 不支持
let [one, two] = [1, 2];
let { x, y } = returnZeroPoint();
function drawText({ text, location: [x, y], bold }: {...}) {}
ArkTS 写法:
let arr: number[] = [1, 2];
let one = arr[0];
let two = arr[1];
let zp = returnZeroPoint();
let x = zp.x;
let y = zp.y;
function drawText(text: string, location: number[], bold: boolean) {
let x = location[0];
let y = location[1];
}
3.4 逗号运算符 , 只允许在 for 里用
for (let i = 0, j = 0; i < 10; ++i, j += 2) {
// ✅ 唯一合法场景
}
// ❌ 这样的写法不允许:x = (++x, x++);
let x = 0;
++x;
x = x++;
4. 类型系统:简化 + 明确(很多 TS 高级类型不能用)
4.1 不支持 structural typing(结构类型)
-
TS:只要 public API 一样,就可以互相赋值。
-
ArkTS:必须有显式关系(继承、implements、类型别名)。
// ✅ ArkTS 推荐:用 interface 抽出共同结构
interface Z { n: number; s: string; }class X implements Z { n: number = 0; s: string = ''; }
class Y implements Z { n: number = 0; s: string = ''; }let x: Z = new X();
let y: Z = new Y();
x = y;
y = x;
4.2 不支持的类型特性(要整体有个印象)
- ❌ 条件类型:
T extends U ? X : Y - ❌
infer - ❌ 映射类型:
{ [K in keyof T]: ... } - ❌ 大部分 Utility Types:
只保留:Partial<T>,Required<T>,Readonly<T>,Record<K, T> - ❌ intersection types:
A & B(用extends继承多个接口代替) - ❌ index signature:
[key: string]: ... - ❌ 索引访问类型:
T[K] - ❌
this类型 - ❌
typeof someVar用作类型(表达式中typeof还可以) - ❌ 字面量类型 +
as const - ❌ JSX
- ❌ 生成器函数
function* - ❌
is类型守卫(用instanceof+as)
可以直接记一句:TS 那些"玩类型体操"的高级类型,大部分 ArkTS 都禁止。
5. 类 / 接口相关规则
5.1 不支持 #private,用 private 关键字
// ❌ TS 写法
class C {
#foo: number = 42;
}
// ✅ ArkTS
class C {
private foo: number = 42;
}
5.2 构造函数中不能"顺便声明属性"
// ❌ TS 写法
class Person {
constructor(private name: string, private age: number) {}
}
// ✅ ArkTS
class Person {
private name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
5.3 类 & 接口的继承/实现限制
-
接口 不能 继承类(只能继承其他接口)。
-
类 不能 被
implements:// ❌ ArkTS class C { foo() {} } class C1 implements C { foo() {} } // ✅ 改成接口 interface C { foo(): void; } class C1 implements C { foo() {} } -
接口不能继承两个里面有 同名方法 的接口(否则方法签名冲突)。
-
不支持声明合并:class / interface / enum 不能写多次 partial 拼起来。
5.4 静态块:一个类里只能有一个 static 块
class C {
static s: string;
static {
C.s = 'aa';
C.s = C.s + 'bb';
}
}
6. 对象 / 数组字面量的规则
6.1 对象字面量必须有显式类型
// ❌ ArkTS 不允许这样"裸字面量"
let o1 = { n: 42, s: 'foo' };
// ✅ ArkTS:显式类型
class C1 { n: number = 0; s: string = ''; }
let o1: C1 = { n: 42, s: 'foo' };
let oo: C1[] = [{ n: 1, s: '1' }, { n: 2, s: '2' }];
6.2 对象字面量不能当"匿名类型声明"
// ❌ ArkTS
let o: { x: number; y: number } = { x: 2, y: 3 };
// ✅ 用 class / interface
class O { x: number = 0; y: number = 0; }
let o: O = { x: 2, y: 3 };
6.3 数组字面量:元素类型必须可推断
class C { n: number = 0; s: string = ''; }
// ✅ 两种写法
let a1 = [{ n: 1, s: '1' } as C, { n: 2, s: '2' } as C]; // C[]
let a2: C[] = [{ n: 1, s: '1' }, { n: 2, s: '2' }]; // C[]
6.4 不能通过 obj['field'] 访问类字段
class Person {
name: string;
age: number;
email: string;
phoneNumber: string;
constructor(name: string, age: number, email: string, phoneNumber: string) {
this.name = name;
this.age = age;
this.email = email;
this.phoneNumber = phoneNumber;
}
}
let p = new Person('John', 30, '***', '18***');
// ❌
console.info(p['name']);
console.info(p['unknownProperty']);
// ✅
console.info(p.name);
7. 函数相关:声明方式 / this / 返回类型
7.1 不支持函数表达式 & 嵌套函数声明
// ❌ 函数表达式
let f = function (s: string) {
console.info(s);
};
// ✅ 用箭头函数
let f = (s: string) => {
console.info(s);
};
// ❌ 函数内声明函数
function addNum(a: number, b: number): void {
function logToConsole(message: string): void {
console.info(message);
}
}
// ✅ 用 lambda
function addNum(a: number, b: number): void {
let logToConsole = (message: string): void => {
console.info(message);
};
}
7.2 this 的使用限制
- this 只能出现在类的实例方法里。
- ❌ 函数体 / 静态方法 / 自由函数中不能用 this。
- 不支持
Function.apply / call / bind(这些都是动态改 this 的)。
7.3 返回类型推断:有些场景必须显式标注
// ❌ ArkTS 要求加返回类型
function f(x: number) { // 必须写: number
if (x <= 0) return x;
return g(x);
}
// ✅
function f(x: number): number {
if (x <= 0) return x;
return g(x);
}
function g(x: number): number { // 这里可以省略返回类型
return f(x - 1);
}
8. 模块系统与 import / export 限制
-
import 必须写在文件最前面 (除了动态
import())。// ✅ import foo from 'module1'; class C {} -
不支持:
requireimport = require('mod')export = ...- ambient module 声明(
declare module 'xxx' {}) - 模块名通配符(
declare module '*!text') - UMD 写法(
export as namespace)
-
.ets可以 import.ets/.ts/.js,但.ts/.js不能 import.ets。
9. 标准库限制 & ESObject 使用收紧
9.1 禁止 / 限制的标准库 API
- 全局:
eval Object的一堆与原型 / 属性描述 / 动态扩展相关的 API 禁用,例如:__proto__、create、defineProperty、setPrototypeOf等。
Reflect、Proxy里面绝大部分也是禁用的。
9.2 ESObject:只能在"跨语言场景"慎用
- 主要场景:动态导入、和
.ts/.js的 any/unknown 对象打交道。 - 不能随便用对象字面量初始化
ESObject。 - 允许点 / 下标访问、调用等,但会有较强限制。
10. 其他零碎但常考的点(记几个关键词)
let而不是var- 不支持
globalThis - 不支持
new.target throw只能抛出Error或其子类- 不支持
with - 不支持
JSX - 不支持 generator(用 async/await)
- 命名空间里不能写可执行语句,只能声明;逻辑放到函数中,再调用。
11. 实战迁移小攻略(你写代码时可以这么用)
- 先全局搜索 :
any,unknown,as const,is(类型守卫),infer,keyof,in,for (let ... in,[key:这种 index signature.
- 对类做一轮"ArkTS 检查" :
- 构造函数里有没有"顺带声明字段"的写法;
- 有没有
#private; - 有没有多个
static {}; - 接口/类有没有声明合并、多次声明。
- 对函数做一轮 :
- 是否有函数表达式 / 嵌套函数;
- 参数有无解构;
- 返回类型哪里是依赖其他调用推断的,要不要手写。
- 对对象/数组字面量做一轮 :
- 对象字面量有没有显式类型;
- 有没有
{x: number, y: number}这种匿名类型; - 数组元素类型能不能直接推断成统一类型。
- 遇到拿不准的高级类型写法(复杂 type/utility) :
- 直接考虑:能不能改成 class + interface + 继承;
- 优先追求"简单、直白、可读",而不是"类型体操"。