20-ArkTs常见错误
20-ArkTs常见错误
arkts-no-ctor-signatures-funcs
在class内声明属性,而不是在构造函数上。
应用代码
class Controller { value: string = '' constructor(value: string) { this.value = value }}
type ControllerConstructor = new (value: string) => Controller;
class Menu { controller: ControllerConstructor = Controller createController() { if (this.controller) { return new this.controller('abc'); } return null; }}
let t = new Menu()console.log(t.createController()!.value)
建议改法
class Controller { value: string = '' constructor(value: string) { this.value = value; }}
type ControllerConstructor = () => Controller;
class Menu { controller: ControllerConstructor = () => { return new Controller('abc') } createController() { if (this.controller) { return this.controller(); } return null; }}
let t: Menu = new Menu();console.log(t.createController()!.value);
arkts-no-globalthis
由于无法为globalThis添加静态类型,只能通过查找的方式访问globalThis的属性,造成额外的性能开销。另外,无法为globalThis的属性标记类型,无法保证对这些属性操作的安全和高性能。因此ArkTS不支持globalThis。
- 建议按照业务逻辑根据import/export语法实现数据在不同模块的传递。
- 必要情况下,可以通过构造的单例对象来实现全局对象的功能。(说明: 不能在har中定义单例对象,har在打包时会在不同的hap中打包两份,无法实现单例。)
构造单例对象
// 构造单例对象export class GlobalContext { private constructor() {} private static instance: GlobalContext; private _objects = new Map<string, Object>();
public static getContext(): GlobalContext { if (!GlobalContext.instance) { GlobalContext.instance = new GlobalContext(); } return GlobalContext.instance; }
getObject(value: string): Object | undefined { return this._objects.get(value); }
setObject(key: string, objectClass: Object): void { this._objects.set(key, objectClass); }}
应用代码
// file1.ts
export class Test { value: string = ''; foo(): void { globalThis.value = this.value; }}
// file2.ts
globalThis.value;
建议改法
// file1.ts
import { GlobalContext } from '../GlobalContext'
export class Test { value: string = ''; foo(): void { GlobalContext.getContext().setObject('value', this.value); }}
// file2.ts
import { GlobalContext } from '../GlobalContext'
GlobalContext.getContext().getObject('value');
arkts-no-func-apply-bind-call
使用标准库中接口
应用代码
let arr: number[] = [1, 2, 3, 4];let str = String.fromCharCode.apply(null, Array.from(arr));
建议改法
let arr: number[] = [1, 2, 3, 4];let str = String.fromCharCode(...Array.from(arr));
bind定义方法
应用代码
class A { value: string = '' foo: Function = () => {}}
class Test { value: string = '1234' obj: A = { value: this.value, foo: this.foo.bind(this) } foo() { console.log(this.value); }}
建议改法1
class A { value: string = '' foo: Function = () => {}}
class Test { value: string = '1234' obj: A = { value: this.value, foo: (): void => this.foo() } foo() { console.log(this.value); }}
建议改法2
class A { value: string = '' foo: Function = () => {}}
class Test { value: string = '1234' foo: () => void = () => { console.log(this.value); } obj: A = { value: this.value, foo: this.foo }}
使用apply
应用代码
class A { value: string; constructor (value: string) { this.value = value; }
foo() { console.log(this.value); }}
let a1 = new A('1');let a2 = new A('2');
a1.foo();a1.foo.apply(a2);
建议改法
class A { value: string; constructor (value: string) { this.value = value; }
foo() { this.fooApply(this); }
fooApply(a: A) { console.log(a.value); }}
let a1 = new A('1');let a2 = new A('2');
a1.foo();a1.fooApply(a2);
arkts-limited-stdlib
Object.fromEntries()
应用代码
let entries = new Map([ ['foo', 123], ['bar', 456]]);
let obj = Object.fromEntries(entries);
建议改法
let entries = new Map([ ['foo', 123], ['bar', 456]]);
let obj: Record<string, Object> = {};entries.forEach((value, key) => { if (key != undefined && key != null) { obj[key] = value; }})
使用Number的属性和方法
ArkTS不允许使用全局对象的属性和方法: Infinity, NaN, isFinite, isNaN, parseFloat, parseInt
可以使用Number的属性和方法: Infinity, NaN, isFinite, isNaN, parseFloat, parseInt
应用代码
NaN;isFinite(123);parseInt('123');
建议改法
Number.NaN;Number.isFinite(123);Number.parseInt('123');
arkts-strict-typing(StrictModeError)
strictPropertyInitialization
应用代码
interface I { name:string}
class A {}
class Test { a: number; b: string; c: boolean; d: I; e: A;}
建议改法
interface I { name:string}
class A {}
class Test { a: number; b: string; c: boolean; d: I = { name:'abc' }; e: A | null = null; constructor(a:number, b:string, c:boolean) { this.a = a; this.b = b; this.c = c; }}
Type *** | null is not assignable to type ***
应用代码
class A { bar() {}}function foo(n: number) { if (n === 0) { return null; } return new A();}function getNumber() { return 5;}let a:A = foo(getNumber());a.bar();
建议改法
class A { bar() {}}function foo(n: number) { if (n === 0) { return null; } return new A();}function getNumber() { return 5;}
let a: A | null = foo(getNumber());a?.bar();
严格属性初始化检查
在class中,如果一个属性没有初始化,且没有在构造函数中被赋值,那么ArkTS将报错。
建议改法
1.一般情况下,建议按照业务逻辑在声明时初始化属性,或者在构造函数中为属性赋值。如:
//code with errorclass Test { value: number flag: boolean}
//方式一,在声明时初始化class Test { value: number = 0 flag: boolean = false}
//方式二,在构造函数中赋值class Test { value: number flag: boolean constructor(value: number, flag: boolean) { this.value = value; this.flag = flag; }}
2.对于对象类型(包括函数类型)A,如果不确定如何初始化,建议按照以下方式之一进行初始化
方式(i) prop: A | null = null
方式(ii) prop?: A
方式三(iii) prop: A | undefined = undefined
- 从性能角度来说,null类型只用在编译期的类型检查中,对虚拟机的性能无影响。而undefined | A被视为联合类型,运行时可能有额外的开销。
- 从代码可读性、简洁性的角度来说,prop?:A是prop: A | undefined = undefined的语法糖,推荐使用可选属性的写法
严格函数类型检查
应用代码
function foo(fn: (value?: string) => void, value: string): void {}
foo((value: string) => {}, ''); //error
建议改法
function foo(fn: (value?: string) => void, value: string): void {}
foo((value?: string) => {}, '');
原因
例如,在以下的例子中,如果编译期不开启严格函数类型的检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。具体来看,在foo的函数体中,一个undefined被传入fn(这是可以的,因为fn可以接受undefined),但是在代码第6行foo的调用点,传入的(value: string) => { console.log(value.toUpperCase()) }的函数实现中,始终将参数value当做string类型,允许其调用toUpperCase方法。如果不开启严格函数类型的检查,那么这段代码在运行时,会出现在undefined上无法找到属性的错误。
function foo(fn: (value?: string) => void, value: string): void { let v: string | undefined = undefined; fn(v);}
foo((value: string) => { console.log(value.toUpperCase()) }, ''); // Cannot read properties of undefined (reading 'toUpperCase')
为了避免运行时的非预期行为,如果在编译时开启了严格类型检查,这段代码将编译不通过,从而可以提醒开发者修改代码,保证程序安全。
严格空值检查
应用代码
class Test { private value?: string public printValue () { console.log(this.value.toLowerCase()); }}
let t = new Test();t.printValue();
建议改法
在编写代码时,建议减少可空类型的使用。如果对变量、属性标记了可空类型,那么在使用它们之间,需要进行空值的判断,根据是否为空值处理不同的逻辑。
class Test { private value?: string
public printValue () { if (this.value) { console.log(this.value.toLowerCase()); } }}
let t = new Test();t.printValue();
原因
在第一段代码中,如果编译期不开启严格空值检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。这是因为t的属性value为undefined(这是因为value?: string是value: string | undefined = undefined的语法糖),在第11行调用printValue方法时,由于在该方法体内未对this.value的值进行空值检查,而直接按照string类型访问其属性,这就导致了运行时的错误。为了避免运行时的非预期行为,如果在编译时开起来严格空值检查,这段代码将编译不通过从而可以提醒开发者修改代码(如按照第二段代码的方式),保证程序安全。
- 0回答
- 6粉丝
- 1关注
- 18-ArkTs常见错误
- 19-ArkTs常见错误
- 21-ArkTs 常见错误
- 22-ArkTs 常见错误
- 17-ArkTs 常见错误
- 鸿蒙Flutter实战:10-常见问题集合
- APP构建错误
- 鸿蒙元服务——部分见解
- JSON.parse 解析错误分析
- 20.HarmonyOS Next CustomSlider组件自定义范围教程(二)
- (六三)HarmonyOS Design 的错误处理与反馈
- 「Mac畅玩鸿蒙与硬件43」UI互动应用篇20 - 闪烁按钮效果
- 个人见解和经验分享:从OpenHarmony看开源技术趋势
- 「Mac玩转仓颉内测版20」PTA刷题篇11 - L1-011 A-B
- 「Mac畅玩鸿蒙与硬件20」鸿蒙UI组件篇10 - Canvas组件自定义绘图