20-ArkTs常见错误

2025-03-31 23:58:27
153次阅读
0个评论

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。

  1. 建议按照业务逻辑根据import/export语法实现数据在不同模块的传递。
  2. 必要情况下,可以通过构造的单例对象来实现全局对象的功能。(说明: 不能在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类型访问其属性,这就导致了运行时的错误。为了避免运行时的非预期行为,如果在编译时开起来严格空值检查,这段代码将编译不通过从而可以提醒开发者修改代码(如按照第二段代码的方式),保证程序安全。

收藏00

登录 后评论。没有帐号? 注册 一个。