ArkTS编程规范参考业界标准及实践,结合ArkTS语言特点,旨在提高代码的规范、安全和性能,适用于开发者使用ArkTS编写代码的系统开发或应用开发场景。
ArkTS在TypeScript基础上强化静态检查和分析,部分规则源于《OpenHarmony应用TS&JS编程指南》,并为ArkTS新增语法添加规则,以提升代码可读性和执行性能。
规则分为要求和建议两个级别。要求原则上应遵从,目前本文所有内容均为针对ArkTS的要求;建议条款属于最佳实践,开发者可根据实际情况考虑是否采用。
能清晰表达意图,避免单个字母或无惯例缩写,使用正确英文单词且符合语法,避免误导。
采用UpperCamelCase风格(首字母大写驼峰命名法),类名应为名词或名词短语,避免使用动词或模糊词。例如:
// 类名 class User { username: string constructor(username: string) { this.username = username; } sayHi() { console.log('hi' + this.username); } } // 枚举名 enum UserType { TEACHER = 0, STUDENT = 1 }; // 命名空间 namespace Base64Utils { function encrypt() { // todo encrypt } function decrypt() { // todo decrypt } };
采用lowerCamelCase风格(小驼峰命名法),函数名通常为动词或动词短语,变量名通常为名词或名词短语。例如:
let msg = 'Hello world'; function sendMsg(msg: string) { // todo send message } let userName = 'Zhangsan'; function findUser(userName: string) { // todo find user by user name }
采用全部大写,单词间用下划线隔开,且尽量表达完整语义。例如:
const MAX_USER_SIZE = 10000; enum UserType { TEACHER = 0, STUDENT = 1 };
布尔型局部变量或方法应加上表达是非意义的前缀(如is、has、can、should等),避免使用否定的布尔变量名。例如:
// 反例 let isNoError = true; let isNotFound = false; function empty() {} function next() {} // 正例 let isError = false; let isFound = true; function isEmpty() {} function hasNext() {}
使用空格缩进,禁止使用tab字符。建议大部分场景用2个空格,换行导致的缩进用4个空格,可在代码编辑器中配置。例如:
class DataSource { id: number = 0 title: string = '' content: string = '' } const dataSource: DataSource[] = [ { id: 1, title: 'Title 1', content: 'Content 1' }, { id: 2, title: 'Title 2', content: 'Content 2' } ]; function test(dataSource: DataSource[]) { if (!dataSource.length) { return; } for (let data of dataSource) { if (!data ||!data.id ||!data.title ||!data.content) { continue; } // some code } // some code }
行宽不超过120个字符,以提升代码可读性,避免过长函数、变量命名和过多嵌套层数。例外情况包括注释包含长命令或URL、预处理error信息等。
if、for、do、while等语句的执行体必须使用大括号,以增强代码清晰度,避免错误。例如:
// 反例 if (condition) console.log('success'); for (let idx = 0; idx < 5; ++idx) console.log(idx); // 正例 if (condition) { console.log('success'); } for (let idx = 0; idx < 5; ++idx) { console.log(idx); }
switch的case和default需缩进一层(2个空格),开关标签后换行的语句再缩进一层(2个空格)。例如:
switch (condition) { case 0: { doSomething(); break; } case 1: { doOtherthing(); break; } default: break; }
当语句过长或可读性不佳时,在合适处换行,将操作符放在行末,保持与格式化工具默认配置一致。例如:
// 假设条件语句超出行宽 if (userCount > MAX_USER_COUNT || userCount < MIN_USER_COUNT) { doSomething(); }
每个语句只声明一个变量,避免写在一行,便于添加变量声明、调试和减少错误。例如:
// 反例 let maxCount = 10, isCompleted = false; let pointX, pointY; pointX = 10; pointY = 0; // 正例 let maxCount = 10; let isCompleted = false; let pointX = 0; let pointY = 0;
空格应突出关键字和重要信息,遵循特定规则,如关键字与左括号间加空格、函数名与参数列表左括号间不加空格等,避免不必要空格和连续空格。例如:
// if 和左括号之间加一个空格 if (isJedi) { fight(); } // 函数名fight和左括号 ( 之间不加空格 function fight(): void { console.log('Swooosh!'); } // else 与其前面的大括号 } 之间增加空格 if (flag) { //... } else { //... } // 函数声明时,左大括号 { 之前加个空格 function foo() { //... } bar('attr', { age: '1 year', sbreed: 'Bernese Mountain Dog', }); // 数组初始化中的逗号后面加个空格,逗号前面不加空格 const arr = [1, 2, 3]; // 函数的多个参数之间的逗号后加个空格,逗号前面不加空格 myFunc(bar, foo, baz);
建议使用单引号,约定俗成。例如:
// 反例 let message = "world"; console.log(message); // 正例 let message = 'world'; console.log(message);
对象字面量属性超过4个时,建议统一换行,要么全换行,要么全在一行。例如:
// 反例 interface I { name: string age: number value: number sum: number foo: boolean bar: boolean } let obj: I = { name: 'tom', age: 16, value: 1, sum: 2, foo: true, bar: false } // 正例 interface I { name: string age: number value: number sum: number foo: boolean bar: boolean } let obj: I = { name: 'tom', age: 16, value: 1, sum: 2, foo: true, bar: false }
写条件语句时,else放在if代码块关闭括号同一行;写异常处理语句时,catch放在try代码块关闭括号同一行。例如:
// 反例 if (isOk) { doThing1(); doThing2(); } else { doThing3(); } try { doSomething(); } catch (err) { // 处理错误 } // 正例 if (isOk) { doThing1(); doThing2(); } else { doThing3(); } try { doSomething(); } catch (err) { // 处理错误 }
大括号应放在控制语句或声明语句同一行,保持一致风格。例如:
// 反例 function foo() { //... } // 正例 function foo() { //... }
建议添加private、protected或public可访问修饰符,提升代码安全性和可读性,注意含private属性的类无法通过对象字面量初始化。例如:
// 反例 class C { count: number = 0 getCount(): number { return this.count } } // 正例 class C { private count: number = 0 public getCount(): number { return this.count } }
不建议省略浮点数小数点前后的0,以提高代码可读性。例如:
// 反例 const num =.5; const num = 2.; const num = -.7; // 正例 const num = 0.5; const num = 2.0; const num = -0.7;
必须使用Number.isNaN()方法判断变量是否为Number.NaN,因为Number.NaN不等于任何值(包括自身),直接比较结果易混淆。例如:
// 反例 if (foo == Number.NaN) { //... } if (foo!= Number.NaN) { //... } // 正例 if (Number.isNaN(foo)) { //... } if (!Number.isNaN(foo)) { //... }
优先使用Array对象方法(如forEach()、map()、every()、filter()、find()、findIndex()、reduce()、some())进行数组遍历处理。例如:
// 反例 const numbers = [1, 2, 3, 4, 5]; // 依赖已有数组来创建新的数组时,通过for遍历,生成一个新数组 const increasedByOne: number[] = []; for (let i = 0; i < numbers.length; i++) { increasedByOne.push(numbers[i] + 1); } // 正例 const numbers = [1, 2, 3, 4, 5]; // better: 使用map方法是更好的方式 const increasedByOne: number[] = numbers.map(num => num + 1);
不要在控制性条件表达式(if、while、for、?:等)中执行赋值操作,以免导致意料之外行为和可读性差。例如:
// 反例 // 在控制性判断中赋值不易理解 if (isFoo = false) { ... } // 正例 const isFoo = someBoolean; // 在上面赋值,if条件判断中直接使用 if (isFoo) { ... }
finally代码块中不要使用return、break、continue或抛出异常,保证其正常结束,否则会影响try或catch代码块中异常抛出和方法返回值。例如:
// 反例 function foo() { try { ... return 1; } catch (err) { ... return 2; } finally { return 3; } } // 正例 function foo() { try { ... return 1; } catch (err) { ... return 2; } finally { console.log('XXX!'); } }
非跨语言调用场景中,避免使用ESObject标注类型,以免引入不必要跨语言调用和性能开销。例如:
// 反例 // lib.ets export interface I { sum: number } export function getObject(value: number): I { let obj: I = { sum: value }; return obj } // app.ets import { getObject } from 'lib' let obj: ESObject = getObject(123); // 正例 // lib.ets export interface I { sum: number } export function getObject(value: number): I { let obj: I = { sum: value }; return obj } // app.ets import { getObject, I } from 'lib' let obj: I = getObject(123);
建议所有数组类型用T[]表示,而非Array,以提高代码可读性。例如:
// 反例 let x: Array<number> = [1, 2, 3]; let y: Array<string> = ['a', 'b', 'c']; // 正例 // 统一使用T[]语法 let x: number[] = [1, 2, 3]; let y: string[] = ['a', 'b', 'c'];
遵循这些ArkTS编程规范,有助于开发者编写规范、高效、易读和易于维护的代码,提升鸿蒙Next应用的整体质量。