当前位置:   article > 正文

HarmonyOS实战开发:适配指导案例_use explicit types instead of "any", "unknown" (ar

use explicit types instead of "any", "unknown" (arkts-no-any-unknown)

本文通过更多应用场景中的案例,提供在ArkTS语法规则下将TS代码适配成ArkTS代码的建议。各章以ArkTS语法规则英文名称命名,每个案例提供适配前的TS代码和适配后的ArkTS代码。

arkts-identifiers-as-prop-names

应用代码

  1. interface W {
  2. bundleName: string
  3. action: string
  4. entities: string[]
  5. }
  6. let wantInfo: W = {
  7. 'bundleName': 'com.huawei.hmos.browser',
  8. 'action': 'ohos.want.action.viewData',
  9. 'entities': ['entity.system.browsable']
  10. }

建议改法

  1. interface W {
  2. bundleName: string
  3. action: string
  4. entities: string[]
  5. }
  6. let wantInfo: W = {
  7. bundleName: 'com.huawei.hmos.browser',
  8. action: 'ohos.want.action.viewData',
  9. entities: ['entity.system.browsable']
  10. }

arkts-no-any-unknown

按照业务逻辑,将代码中的any, unknown改为具体的类型

  1. function printObj(obj: any) {
  2. console.log(obj)
  3. }
  4. printObj('abc')

建议改法

  1. function printObj(obj: string) {
  2. console.log(obj)
  3. }
  4. printObj('abc')

标注JSON.parse返回值类型

应用代码

  1. class A {
  2. v: number = 0
  3. s: string = ''
  4. foo(str: string) {
  5. let tmpStr = JSON.parse(str)
  6. if (tmpStr.add != undefined) {
  7. this.v = tmpStr.v
  8. this.s = tmpStr.s
  9. }
  10. }
  11. }

建议改法

  1. class A {
  2. v: number = 0
  3. s: string = ''
  4. foo(str: string) {
  5. let tmpStr: Record<string, Object> = JSON.parse(str)
  6. if (tmpStr.add != undefined) {
  7. this.v = tmpStr.v as number
  8. this.s = tmpStr.s as string
  9. }
  10. }
  11. }

使用Record类型

应用代码

  1. function printProperties(obj: any) {
  2. console.log(obj.name)
  3. console.log(obj.value)
  4. }

建议改法

  1. function printProperties(obj: Record<string, Object>) {
  2. console.log(obj.name)
  3. console.log(obj.value)
  4. }

arkts-no-call-signature

使用函数类型来替代。

应用代码

  1. interface I {
  2. (value: string): void;
  3. }
  4. function foo(fn: I) {
  5. fn('abc')
  6. }
  7. foo((value: string) => {
  8. console.log(value)
  9. })

建议改法

  1. type I = (value: string) => void
  2. function foo(fn: I) {
  3. fn('abc')
  4. }
  5. foo((value: string) => {
  6. console.log(value)
  7. })

arkts-no-ctor-signatures-type

应用代码

  1. class Controller {
  2. value: number = 0
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. type ControllerConstrucotr = {
  8. new (value: number): Controller;
  9. }
  10. class Menu {
  11. controller: ControllerConstrucotr = Controller
  12. createController() {
  13. if (this.controller) {
  14. return new this.controller(123)
  15. }
  16. return null;
  17. }
  18. }
  19. let t = new Menu()
  20. console.log(t.createController()!.value)

建议改法

  1. class Controller {
  2. value: number = 0
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. type ControllerConstrucotr = () => Controller;
  8. class Menu {
  9. controller: ControllerConstrucotr = () => {
  10. return new Controller(123)
  11. }
  12. createController() {
  13. if (this.controller) {
  14. return this.controller()
  15. }
  16. return null;
  17. }
  18. }
  19. let t: Menu = new Menu()
  20. console.log(t.createController()!.value)

arkts-no-indexed-signatures

使用Record类型来替代。

应用代码

  1. function foo(data: { [key: string]: string }) {
  2. data['a'] = 'a'
  3. data['b'] = 'b'
  4. data['c'] = 'c'
  5. }

建议改法

  1. function foo(data: Record<string, string>) {
  2. data['a'] = 'a'
  3. data['b'] = 'b'
  4. data['c'] = 'c'
  5. }

arkts-no-typing-with-this

应用代码

  1. class C {
  2. getInstance(): this {
  3. return this
  4. }
  5. }

建议改法

  1. class C {
  2. getInstance(): C {
  3. return this
  4. }
  5. }

arkts-no-ctor-prop-decls

应用代码

  1. class Person {
  2. constructor(readonly name: string) {}
  3. getName(): string {
  4. return this.name
  5. }
  6. }

建议改法

  1. class Person {
  2. name: string
  3. constructor(name: string) {
  4. this.name = name
  5. }
  6. getName(): string {
  7. return this.name
  8. }
  9. }

arkts-no-ctor-signatures-iface

应用代码

  1. class Controller {
  2. value: number = 0
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. interface ControllerConstrucotr {
  8. new (value: number): Controller;
  9. }
  10. class Menu {
  11. controller: ControllerConstrucotr = Controller
  12. createController() {
  13. if (this.controller) {
  14. return new this.controller(123)
  15. }
  16. return null;
  17. }
  18. }
  19. let t = new Menu()
  20. console.log(t.createController()!.value)

建议改法

  1. class Controller {
  2. value: number = 0
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. type ControllerConstrucotr = () => Controller;
  8. class Menu {
  9. controller: ControllerConstrucotr = () => {
  10. return new Controller(123)
  11. }
  12. createController() {
  13. if (this.controller) {
  14. return this.controller()
  15. }
  16. return null;
  17. }
  18. }
  19. let t: Menu = new Menu()
  20. console.log(t.createController()!.value)

arkts-no-props-by-index

可以转换成Record类型,用来访问对象的属性。

应用代码

  1. import myRouter from '@ohos.router';
  2. let params: Object = myRouter.getParams();
  3. let funNum: number = params['funNum'];
  4. let target: string = params['target'];

建议改法

  1. import myRouter from '@ohos.router';
  2. let params = myRouter.getParams() as Record<string, string | number>;
  3. let funNum: number = params.funNum as number;
  4. let target: string = params.target as string;

arkts-no-inferred-generic-params

应用代码

  1. class A {
  2. str: string = ''
  3. }
  4. class B extends A {}
  5. class C extends A {}
  6. let arr: Array<A> = []
  7. let originMenusMap:Map<string, C> = new Map(arr.map(item => [item.str, (item instanceof C) ? item : null]))

建议改法

  1. class A {
  2. str: string = ''
  3. }
  4. class B extends A {}
  5. class C extends A {}
  6. let arr: Array<A> = []
  7. let originMenusMap: Map<string, C | null> = new Map<string, C | null>(arr.map<[string, C | null]>(item => [item.str, (item instanceof C) ? item : null]))

原因

(item instanceof C) ? item : null 需要声明类型为,由于编译器无法推导出的泛型类型参数,需要显式标注。C | nullmap

arkts-no-regexp-literals

应用代码

let regex: RegExp = /\s*/g

建议改法

let regexp: RegExp = new RegExp('\\s*','g')

原因

如果正则表达式中使用了标志符,需要将其作为的参数。new RegExp()

arkts-no-untyped-obj-literals

从SDK中导入类型,标注object literal类型

应用代码

  1. const area = {
  2. pixels: new ArrayBuffer(8),
  3. offset: 0,
  4. stride: 8,
  5. region: { size: { height: 1,width:2 }, x: 0, y: 0 }
  6. }

建议改法

  1. import image from '@ohos.multimedia.image';
  2. const area: image.PositionArea = {
  3. pixels: new ArrayBuffer(8),
  4. offset: 0,
  5. stride: 8,
  6. region: { size: { height: 1, width: 2 }, x: 0, y: 0 }
  7. }

用class为object literal标注类型,需要class的构造函数无参数

应用代码

  1. class Test {
  2. value: number = 1
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. let t: Test = { value: 2 }

建议改法1

  1. // 去除构造函数
  2. class Test {
  3. value: number = 1
  4. }
  5. let t: Test = { value: 2 }

建议改法2

  1. // 使用new
  2. class Test {
  3. value: number = 1
  4. constructor(value: number) {
  5. this.value = value
  6. }
  7. }
  8. let t: Test = new Test(2)

原因

  1. class C {
  2. value: number = 1
  3. constructor(n: number) {
  4. if (n < 0) {
  5. throw new Error('Negative')
  6. }
  7. this.value = n
  8. }
  9. }
  10. let s: C = new C(-2) //抛出异常
  11. let t: C = { value : -2 } //ArkTS不支持

例如在上面的例子中,如果允许使用来标注object literal的类型,那么上述代码中的变量会导致行为的二义性。ArkTS禁止通过object literal来绕过这一行为。Ct

用class/interface为object literal标注类型,需要使用identifier作为object literal的key

应用代码

  1. class Test {
  2. value: number = 0
  3. }
  4. let arr: Test[] = [
  5. {
  6. 'value': 1
  7. },
  8. {
  9. 'value': 2
  10. },
  11. {
  12. 'value': 3
  13. }
  14. ]

建议改法

  1. class Test {
  2. value: number = 0
  3. }
  4. let arr: Test[] = [
  5. {
  6. value: 1
  7. },
  8. {
  9. value: 2
  10. },
  11. {
  12. value: 3
  13. }
  14. ]

使用Record为object literal标注类型,需要使用字符串作为object literal的key

应用代码

  1. let obj: Record<string, number | string> = {
  2. value: 123,
  3. name: 'abc'
  4. }

建议改法

  1. let obj: Record<string, number | string> = {
  2. 'value': 123,
  3. 'name': 'abc'
  4. }

函数参数类型包含index signature

应用代码

  1. function foo(obj: { [key: string]: string}): string {
  2. if (obj != undefined && obj != null) {
  3. return obj.value1 + obj.value2
  4. }
  5. return ''
  6. }

建议改法

  1. function foo(obj: Record<string, string>): string {
  2. if (obj != undefined && obj != null) {
  3. return obj.value1 + obj.value2
  4. }
  5. return ''
  6. }

函数实参使用了object literal

应用代码

  1. (fn) => {
  2. fn({ value: 123, name:'' })
  3. }

建议改法

  1. class T {
  2. value: number = 0
  3. name: string = ''
  4. }
  5. (fn: (v: T) => void) => {
  6. fn({ value: 123, name: '' })
  7. }

class/interface 中包含方法

应用代码

  1. interface T {
  2. foo(value: number): number
  3. }
  4. let t: T = { foo: (value) => { return value } }

建议改法1

  1. interface T {
  2. foo: (value: number) => number
  3. }
  4. let t:T = { foo: (value) => { return value } }

建议改法2

  1. class T {
  2. foo: (value: number) => number = (value: number) => {
  3. return value
  4. }
  5. }
  6. let t:T = new T()

原因

class/interface中声明的方法应该被所有class的实例共享。ArkTS不支持通过object literal改写实例方法。ArkTS支持函数类型的属性。

export default对象

应用代码

  1. import hilog from '@ohos.hilog'
  2. export default {
  3. onCreate() {
  4. hilog.info(0x0000, 'testTag', '%{public}s', 'Application onCreate');
  5. },
  6. onDestroy() {
  7. hilog.info(0x0000, 'testTag', '%{public}s', 'Application onDestroy')
  8. }
  9. }

建议改法

  1. import hilog from '@ohos.hilog'
  2. class Test {
  3. onCreate() {
  4. hilog.info(0x0000, 'testTag', '%{public}s', 'Application onCreate');
  5. }
  6. onDestroy() {
  7. hilog.info(0x0000, 'testTag', '%{public}s', 'Application onDestroy')
  8. }
  9. }
  10. export default new Test()

通过导入namespace获取类型

应用代码

  1. import { BusinessError } from '@ohos.base';
  2. import bluetooth from '@ohos.bluetooth';
  3. let serverNumber = -1;
  4. function serverSocket(code: BusinessError, num: number) {
  5. console.log('bluetooth error code: ' + code.code);
  6. if (code.code == 0) {
  7. console.log('bluetooth serverSocket Number: ' + num);
  8. serverNumber = num;
  9. }
  10. }
  11. let sppOption = { uuid: '', secure: false, type: 0 };
  12. bluetooth.sppListen('', sppOption, serverSocket);

建议改法

  1. import { BusinessError } from '@ohos.base';
  2. import bluetooth from '@ohos.bluetooth';
  3. let serverNumber = -1;
  4. function serverSocket(code: BusinessError, num: number) {
  5. console.log('bluetooth error code: ' + code.code);
  6. if (code.code == 0) {
  7. console.log('bluetooth serverSocket Number: ' + num);
  8. serverNumber = num;
  9. }
  10. }
  11. let sppOption: bluetooth.SppOption = { uuid: '', secure: false, type: 0 };
  12. bluetooth.sppListen('', sppOption, serverSocket);

原因

对象字面量缺少类型,根据分析可以得知,的类型来源于SDK,那么只需要将类型导入即可。 注意到在中,是定义在namespace中的,所以在ets文件中,先导入namespace,再通过名称获取相应的类型。bluetooth.sppListensppOption@ohos.bluetoothsppOption

object literal传参给Object类型

应用代码

  1. function emit(event: string, ...args: Object[]): void {}
  2. emit('', {
  3. 'action': 11,
  4. 'outers': false
  5. })

建议改法

  1. function emit(event: string, ...args: Object[]): void {}
  2. let emitArg: Record<string, number | boolean> = {
  3. 'action': 11,
  4. 'outers': false
  5. }
  6. emit('', emitArg)

arkts-no-obj-literals-as-types

应用代码

type Person = { name: string, age: number }

建议改法

  1. interface Person {
  2. name: string,
  3. age: number
  4. }

arkts-no-noninferrable-arr-literals

应用代码

  1. let permissionList = [
  2. { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' },
  3. { name: '麦克风', value: '用于反馈问题单时增加语音' },
  4. { name: '存储', value: '用于反馈问题单时增加本地文件附件' }
  5. ]

建议改法

为对象字面量声明类型

  1. class PermissionItem {
  2. name?: string
  3. value?: string
  4. }
  5. let permissionList: PermissionItem[] = [
  6. { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' },
  7. { name: '麦克风', value: '用于反馈问题单时增加语音' },
  8. { name: '存储', value: '用于反馈问题单时增加本地文件附件' }
  9. ]

arkts-no-method-reassignment

应用代码

  1. class C {
  2. add(left: number, right: number): number {
  3. return left + right;
  4. }
  5. }
  6. function sub(left: number, right: number): number {
  7. return left - right;
  8. }
  9. let c1 = new C()
  10. c1.add = sub

建议改法

  1. class C {
  2. add: (left: number, right: number) => number =
  3. (left: number, right: number) => {
  4. return left + right;
  5. }
  6. }
  7. function sub(left: number, right: number): number {
  8. return left - right;
  9. }
  10. let c1 = new C()
  11. c1.add = sub

arkts-no-polymorphic-unops

应用代码

  1. let a = +'5'
  2. let b = -'5'
  3. let c = ~'5'
  4. let d = +'string'

建议改法

  1. let a = Number.parseInt('5')
  2. let b = -Number.parseInt('5')
  3. let c = ~Number.parseInt('5')
  4. let d = new Number('string')

arkts-no-type-query

应用代码

  1. // module1.ts
  2. class C {
  3. value: number = 0
  4. }
  5. export let c = new C()
  6. // module2.ts
  7. import { c } from './module1'
  8. let t: typeof c = { value: 123 }

建议改法

  1. // module1.ts
  2. class C {
  3. value: number = 0
  4. }
  5. export { C }
  6. // module2.ts
  7. import { C } from './module1'
  8. let t: C = { value: 123 }

arkts-no-in

应用代码

  1. let arr = [10, 20, 30, 40]
  2. let isIn = 5 in arr

建议改法

  1. let arr = [10, 20, 30, 40]
  2. let isIn = 5 < arr.length

arkts-no-destruct-assignment

应用代码

  1. let map = new Map<string, string>([['a', 'a'], ['b', 'b']])
  2. for (let [key, value] of map) {
  3. console.log(key)
  4. console.log(value)
  5. }

建议改法

使用数组

  1. let map = new Map<string, string>([['a', 'a'], ['b', 'b']])
  2. for (let arr of map) {
  3. let key = arr[0]
  4. let value = arr[1]
  5. console.log(key)
  6. console.log(value)
  7. }

arkts-no-types-in-catch

应用代码

  1. import { BusinessError } from '@ohos.base';
  2. try {
  3. // ...
  4. } catch (e: BusinessError) {
  5. logger.error(e.code, e.message);
  6. }

建议改法

  1. import { BusinessError } from '@ohos.base';
  2. try {
  3. // ...
  4. } catch (error) {
  5. let e: BusinessError = error as BusinessError;
  6. logger.error(e.code, e.message);
  7. }

arkts-no-for-in

应用代码

  1. interface Person {
  2. [name: string]: string
  3. }
  4. let p: Person = {
  5. name: 'tom',
  6. age: '18'
  7. };
  8. for (let t in p) {
  9. console.log(p[t])
  10. }

建议改法

  1. let p: Record<string, string> = {
  2. 'name': 'tom',
  3. 'age': '18'
  4. };
  5. for (let ele of Object.entries(p)) {
  6. console.log(ele[1])
  7. }

arkts-no-mapped-types

应用代码

  1. class C {
  2. a: number = 0
  3. b: number = 0
  4. c: number = 0
  5. }
  6. type OptionsFlags = {
  7. [Property in keyof C]: string
  8. }

建议改法

  1. class C {
  2. a: number = 0
  3. b: number = 0
  4. c: number = 0
  5. }
  6. type OptionsFlags = Record<keyof C, string>

arkts-limited-throw

应用代码

  1. import { BusinessError } from '@ohos.base';
  2. function ThrowError(error: BusinessError) {
  3. throw error
  4. }

建议改法

  1. import { BusinessError } from '@ohos.base';
  2. function ThrowError(error: BusinessError) {
  3. throw error as Error
  4. }

原因

throw语句中值的类型必须为或者其继承类,如果继承类是一个泛型,会有编译期报错。建议使用将类型转换为。ErrorasError

arkts-no-standalone-this

函数内使用this

应用代码

  1. function foo() {
  2. console.log(this.value)
  3. }
  4. let obj = { value: 123 }
  5. foo.apply(obj)

建议改法1

使用类的方法实现,如果该方法被多个类使用,可以考虑采用继承的机制

  1. class Test {
  2. value: number = 0
  3. constructor (value: number) {
  4. this.value = value
  5. }
  6. foo() {
  7. console.log(this.value)
  8. }
  9. }
  10. let obj: Test = new Test(123)
  11. obj.foo()

建议改法2

将this作为参数传入

  1. function foo(obj: Test) {
  2. console.log(obj.value)
  3. }
  4. class Test {
  5. value: number = 0
  6. }
  7. let obj: Test = { value: 123 }
  8. foo(obj)

建议改法3

将属性作为参数传入

  1. function foo(value: number) {
  2. console.log(value)
  3. }
  4. class Test {
  5. value: number = 0
  6. }
  7. let obj: Test = { value: 123 }
  8. foo(obj.value)

class的静态方法内使用this

应用代码

  1. class Test {
  2. static value: number = 123
  3. static foo(): number {
  4. return this.value
  5. }
  6. }

建议改法

  1. class Test {
  2. static value: number = 123
  3. static foo(): number {
  4. return Test.value
  5. }
  6. }

arkts-no-spread

应用代码

  1. import notification from '@ohos.notificationManager';
  2. function buildNotifyLongRequest(): notification.NotificationRequest {
  3. // ...
  4. }
  5. let notificationRequest: notification.NotificationRequest = {
  6. ...buildNotifyLongRequest(),
  7. deliveryTime: new Date().getTime()
  8. }

建议改法

  1. import notification from '@ohos.notificationManager';
  2. function buildNotifyLongRequest():notification.NotificationRequest {
  3. // ...
  4. }
  5. let notificationRequest: notification.NotificationRequest = buildNotifyLongRequest();
  6. notificationRequest.deliveryTime = new Date().getTime();

原因

ArkTS中,对象布局在编译期是确定的。如果需要将一个对象的所有属性展开赋值给另一个对象可以通过逐个属性赋值语句完成。在本例中,需要展开的对象和赋值的目标对象类型恰好相同,可以通过改变该对象属性的方式重构代码。

arkts-no-ctor-signatures-funcs

在class内声明属性,而不是在构造函数上。

应用代码

  1. class Controller {
  2. value: number = 0
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. type ControllerConstrucotr = new (value: number) => Controller;
  8. class Menu {
  9. controller: ControllerConstrucotr = Controller
  10. createController() {
  11. if (this.controller) {
  12. return new this.controller(123)
  13. }
  14. return null;
  15. }
  16. }
  17. let t = new Menu()
  18. console.log(t.createController()!.value)

建议改法

  1. class Controller {
  2. value: number = 0
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. type ControllerConstrucotr = () => Controller;
  8. class Menu {
  9. controller: ControllerConstrucotr = () => { return new Controller(123) }
  10. createController() {
  11. if (this.controller) {
  12. return this.controller()
  13. }
  14. return null;
  15. }
  16. }
  17. let t: Menu = new Menu()
  18. console.log(t.createController()!.value)

arkts-no-globalthis

由于无法为globalThis添加静态类型,只能通过查找的方式访问globalThis的属性,造成额外的性能开销。另外,无法为globalThis的属性标记类型,无法保证对这些属性操作的安全和高性能。因此ArkTS不支持globalThis。

  1. 建议按照业务逻辑根据语法实现数据在不同模块的传递。import/export

  2. 必要情况下,可以通过构造的单例对象来实现全局对象的功能。(说明:不能在har中定义单例对象,har在打包时会在不同的hap中打包两份,无法实现单例。)

构造单例对象

  1. // 构造单例对象
  2. export class GlobalContext {
  3. private constructor() {}
  4. private static instance: GlobalContext;
  5. private _objects = new Map<string, Object>();
  6. public static getContext(): GlobalContext {
  7. if (!GlobalContext.instance) {
  8. GlobalContext.instance = new GlobalContext();
  9. }
  10. return GlobalContext.instance;
  11. }
  12. getObject(value: string): Object | undefined {
  13. return this._objects.get(value);
  14. }
  15. setObject(key: string, objectClass: Object): void {
  16. this._objects.set(key, objectClass);
  17. }
  18. }

应用代码

  1. // file1.ts
  2. export class Test {
  3. value: string = '';
  4. foo(): void {
  5. globalThis.value = this.value;
  6. }
  7. }
  8. // file2.ts
  9. print(globalThis.value)

建议改法

  1. // file1.ts
  2. import { GlobalContext } from '../GlobalContext'
  3. export class Test {
  4. value: string = '';
  5. foo(): void {
  6. GlobalContext.getContext().setObject('value', this.value);
  7. }
  8. }
  9. // file2.ts
  10. import { GlobalContext } from '../GlobalContext'
  11. console.log(GlobalContext.getContext().getObject('value'));

arkts-no-func-apply-bind-call

使用标准库中接口

应用代码

  1. let arr: number[] = [1, 2, 3, 4]
  2. let str = String.fromCharCode.apply(null, Array.from(arr))

建议改法

  1. let arr: number[] = [1, 2, 3, 4]
  2. let str = String.fromCharCode(...Array.from(arr))

bind定义方法

应用代码

  1. class A {
  2. value: number = 0
  3. foo: Function = () => {}
  4. }
  5. class Test {
  6. value: number = 1234
  7. obj: A = {
  8. value: this.value,
  9. foo: this.foo.bind(this)
  10. }
  11. foo() {
  12. console.log(this.value)
  13. }
  14. }

建议改法1

  1. class A {
  2. value: number = 0
  3. foo: Function = () => {}
  4. }
  5. class Test {
  6. value: number = 1234
  7. obj: A = {
  8. value: this.value,
  9. foo: (): void => this.foo()
  10. }
  11. foo() {
  12. console.log(this.value)
  13. }
  14. }

建议改法2

  1. class A {
  2. value: number = 0
  3. foo: Function = () => {}
  4. }
  5. class Test {
  6. value: number = 1234
  7. foo: () => void = () => {
  8. console.log(this.value)
  9. }
  10. obj: A = {
  11. value: this.value,
  12. foo: this.foo
  13. }
  14. }

使用apply

应用代码

  1. class A {
  2. value:number;
  3. constructor (value:number) {
  4. this.value = value
  5. }
  6. foo() {
  7. console.log(this.value)
  8. }
  9. }
  10. let a1 = new A(1)
  11. let a2 = new A(2)
  12. a1.foo()
  13. a1.foo.apply(a2)

建议改法

  1. class A {
  2. value:number;
  3. constructor (value:number) {
  4. this.value = value
  5. }
  6. foo() {
  7. this.fooApply(this)
  8. }
  9. fooApply(a: A) {
  10. console.log(a.value)
  11. }
  12. }
  13. let a1 = new A(1)
  14. let a2 = new A(2)
  15. a1.foo()
  16. a1.fooApply(a2)

arkts-limited-stdlib

ArkTS不允许使用全局对象的属性和方法: Infinity, NaN, isFinite, isNaN, parseFloat, parseInt

可以使用的属性和方法: NumberInfinity, NaN, isFinite, isNaN, parseFloat, parseInt

应用代码

  1. console.log(NaN)
  2. console.log(isFinite(123))
  3. console.log(parseInt('123'))

建议改法

  1. console.log(Number.NaN)
  2. console.log(Number.isFinite(123))
  3. console.log(Number.parseInt('123'))

arkts-strict-typing(StrictModeError)

strictPropertyInitialization

应用代码

  1. interface I {
  2. name:string
  3. }
  4. class A {}
  5. class Test {
  6. a: number;
  7. b: string;
  8. c: boolean;
  9. d: I;
  10. e: A;
  11. }

建议改法

  1. interface I {
  2. name:string
  3. }
  4. class A {}
  5. class Test {
  6. a: number;
  7. b: string;
  8. c: boolean;
  9. d: I = { name:'abc' };
  10. e: A | null = null;
  11. constructor(a:number, b:string, c:boolean) {
  12. this.a = a;
  13. this.b = b;
  14. this.c = c;
  15. }
  16. }

Type *** | null is not assignable to type ***

应用代码

  1. class A {
  2. bar() {}
  3. }
  4. function foo(n: number) {
  5. if (n === 0) {
  6. return null;
  7. }
  8. return new A();
  9. }
  10. function getNumber() {
  11. return 5;
  12. }
  13. let a:A = foo(getNumber());
  14. a.bar();

建议改法

  1. class A {
  2. bar() {}
  3. }
  4. function foo(n: number) {
  5. if (n === 0) {
  6. return null;
  7. }
  8. return new A();
  9. }
  10. function getNumber() {
  11. return 5;
  12. }
  13. let a: A | null = foo(getNumber());
  14. a?.bar();

严格属性初始化检查

在class中,如果一个属性没有初始化,且没有在构造函数中被赋值,那么ArkTS将报错。

建议改法

1.一般情况下,建议按照业务逻辑在声明时初始化属性,或者在构造函数中为属性赋值。如:

  1. //code with error
  2. class Test {
  3. value: nameber
  4. flag: boolean
  5. }
  6. //方式一,在声明时初始化
  7. class Test {
  8. value: number = 0
  9. flag: boolean = false
  10. }
  11. //方式二,在构造函数中赋值
  12. class Test {
  13. value: number
  14. flag: boolean
  15. constructor(value: number, flag: boolean) {
  16. this.value = value
  17. this.flag = flag
  18. }
  19. }

2.对于对象类型(包括函数类型),如果不确定如何初始化,建议按照以下方式之一进行初始化A

​ 方式(i) prop: A | null = null

​ 方式(ii) prop?: A

​ 方式三(iii) prop: A | undefined = undefined

  • 从性能角度来说,类型只用在编译期的类型检查中,对虚拟机的性能无影响。而被视为联合类型,运行时可能有额外的开销。nullundefined | A
  • 从代码可读性、简洁性的角度来说,是的语法糖,推荐使用可选属性的写法prop?:Aprop: A | undefined = undefined

严格函数类型检查

应用代码

  1. function foo(fn: (value?: string) => void, value: string): void {}
  2. foo((value: string) => {}, '') //error

建议改法

  1. function foo(fn: (value?: string) => void, value: string): void {}
  2. foo((value?: string) => {}, '')

原因

例如,在以下的例子中,如果编译期不开启严格函数类型的检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。具体来看,在的函数体中,一个被传入(这是可以的,因为可以接受),但是在代码第6行的调用点,传入的的函数实现中,始终将参数当做string类型,允许其调用方法。如果不开启严格函数类型的检查,那么这段代码在运行时,会出现在上无法找到属性的错误。fooundefinedfnfnundefinedfoo(value: string) => { console.log(value.toUpperCase()) }valuetoUpperCaseundefined

  1. function foo(fn: (value?: string) => void, value: string): void {
  2. let v: string | undefined = undefined
  3. fn(v)
  4. }
  5. foo((value: string) => { console.log(value.toUpperCase()) }, '') // Cannot read properties of undefined (reading 'toUpperCase')

为了避免运行时的非预期行为,如果在编译时开启了严格类型检查,这段代码将编译不通过,从而可以提醒开发者修改代码,保证程序安全。

严格空值检查

应用代码

  1. class Test {
  2. private value?: string
  3. public printValue () {
  4. console.log(this.value.toLowerCase())
  5. }
  6. }
  7. let t = new Test()
  8. t.printValue()

建议改法

在编写代码时,建议减少可空类型的使用。如果对变量、属性标记了可空类型,那么在使用它们之间,需要进行空值的判断,根据是否为空值处理不同的逻辑。

  1. class Test {
  2. private value?: string
  3. public printValue () {
  4. if (this.value) {
  5. console.log(this.value.toLowerCase())
  6. }
  7. }
  8. }
  9. let t = new Test()
  10. t.printValue()

原因

在第一段代码中,如果编译期不开启严格空值检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。这是因为的属性为(这是因为是的语法糖),在第11行调用方法时,由于在该方法体内未对的值进行空值检查,而直接按照类型访问其属性,这就导致了运行时的错误。为了避免运行时的非预期行为,如果在编译时开起来严格空值检查,这段代码将编译不通过从而可以提醒开发者修改代码(如按照第二段代码的方式),保证程序安全。tvalueundefinedvalue?: stringvalue : string | undefined = undefinedprintValuethis.valuestring

函数返回类型不匹配

应用代码

  1. class Test {
  2. handleClick: (action: string, externInfo?: DiaExternInfo) => void | null = null;
  3. }

建议改法

在这种写法下,函数返回类型被解析为 ,需要添加括号用来区分union类型。void | undefined

  1. class Test {
  2. handleClick: ((action: string, externInfo?: DialogExternInfo) => void) | null = null;
  3. }

'***' is of type 'unknown'

应用代码

  1. try {
  2. } catch (error) {
  3. console.log(error.message)
  4. }

建议改法

  1. import { BusinessError } from '@ohos.base'
  2. try {
  3. } catch (error) {
  4. console.log((error as BusinessError).message)
  5. }

Type '*** | null' is not assignable to type '***'

应用代码

  1. class A {
  2. value: number
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. function foo(v: number): A | null {
  8. if (v > 0) {
  9. return new A(v)
  10. }
  11. return null
  12. }
  13. let a: A = foo()

建议改法1

修改变量的类型:。alet a: A | null = foo()

  1. class A {
  2. value: number
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. function foo(v: number): A | null {
  8. if (v > 0) {
  9. return new A(v)
  10. }
  11. return null
  12. }
  13. let a: A | null = foo(123)
  14. if (a != null) {
  15. // 非空分支
  16. } else {
  17. // 处理null
  18. }

建议改法2

如果可以断定此处调用一定返回非空值,可以使用非空断言。foo!

  1. class A {
  2. value: number
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. function foo(v: number): A | null {
  8. if (v > 0) {
  9. return new A(v)
  10. }
  11. return null
  12. }
  13. let a: A = foo(123)!

Cannot invoke an object which possibly 'undefined'

应用代码

  1. interface A {
  2. foo?: () => void
  3. }
  4. let a:A = { foo: () => {} }
  5. a.foo()

建议改法1

  1. interface A {
  2. foo: () => void
  3. }
  4. let a: A = { foo: () => {} }
  5. a.foo()

建议改法2

  1. interface A {
  2. foo?: () => void
  3. }
  4. let a: A = { foo: () => {} }
  5. if (a.foo) {
  6. a.foo()
  7. }

原因

在原先代码的定义中,foo是可选属性,有可能为undefined,对undefined的调用会导致报错。建议按照业务逻辑判断是否需要为可选属性。如果确实需要,那么在访问到该属性后需要进行空值检查。

Variable '***' is used before being assigned

应用代码

  1. class Test {
  2. value: number = 0
  3. }
  4. let a: Test
  5. try {
  6. a = { value: 1}
  7. } catch (e) {
  8. a.value
  9. }
  10. a.value

建议改法

  1. class Test {
  2. value: number = 0
  3. }
  4. let a: Test | null = null
  5. try {
  6. a = { value:1 }
  7. } catch (e) {
  8. if (a) {
  9. a.value
  10. }
  11. }
  12. if (a) {
  13. a.value
  14. }

原因

对于primitive types,可以根据业务逻辑赋值,例如0,'',false。

对于对象类型,可以将类型修改为和null的联合类型,并赋值null,使用时需要进行非空检查。

Function lacks ending return statement and return type does not include 'undefined'.

应用代码

  1. function foo(a: number): number {
  2. if (a > 0) {
  3. return a
  4. }
  5. }

建议改法1

根据业务逻辑,在else分支中返回合适的数值

建议改法2

  1. function foo(a: number): number | undefined {
  2. if (a > 0) {
  3. return a
  4. }
  5. return
  6. }

arkts-strict-typing-required

应用代码

  1. // @ts-nocheck
  2. var a: any = 123

建议改法

let a: number = 123

原因

ArkTS不支持通过注释的方式绕过严格类型检查。首先将注释(或者)删去,再根据报错信息修改其他代码。// @ts-nocheck// @ts-ignore

Importing ArkTS files to JS and TS files is not allowed

arkts-no-tsdeps

不允许.ts、.js文件.ets文件源码。import

建议改法

方式1.将.ts文件的后缀修改成ets,按照ArkTS语法规则适配代码。

方式2.将.ets文件中被.ts文件依赖的代码单独抽取到.ts文件中。

arkts-no-special-imports

应用代码

import type {A, B, C, D } from '***'

建议改法

import {A, B, C, D } from '***'

arkts-no-classes-as-obj

使用class构造实例

应用代码

  1. class Controller {
  2. value: number = 0
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. interface ControllerConstrucotr {
  8. new (value: number): Controller;
  9. }
  10. class Menu {
  11. controller: ControllerConstrucotr = Controller
  12. createController() {
  13. if (this.controller) {
  14. return new this.controller(123)
  15. }
  16. return null;
  17. }
  18. }
  19. let t = new Menu()
  20. console.log(t.createController()!.value)

建议改法

  1. class Controller {
  2. value: number = 0
  3. constructor(value: number) {
  4. this.value = value
  5. }
  6. }
  7. type ControllerConstrucotr = () => Controller;
  8. class Menu {
  9. controller: ControllerConstrucotr = () => { return new Controller(123) }
  10. createController() {
  11. if (this.controller) {
  12. return this.controller()
  13. }
  14. return null;
  15. }
  16. }
  17. let t: Menu = new Menu()
  18. console.log(t.createController()!.value)

访问静态属性

应用代码

  1. class C1 {
  2. static value: string = 'abc'
  3. }
  4. class C2 {
  5. static value: string = 'def'
  6. }
  7. function getValue(obj: any) {
  8. return obj['value']
  9. }
  10. console.log(getValue(C1))
  11. console.log(getValue(C2))

建议改法

  1. class C1 {
  2. static value: string = 'abc'
  3. }
  4. class C2 {
  5. static value: string = 'def'
  6. }
  7. function getC1Value(): string {
  8. return C1.value;
  9. }
  10. function getC2Value(): string {
  11. return C2.value;
  12. }
  13. console.log(getC1Value())
  14. console.log(getC2Value())

arkts-no-side-effects-imports

改用动态import

应用代码

import 'module'

建议改法

import('module')

arkts-no-func-props

应用代码

  1. function foo(value: number): void {
  2. console.log(value.toString())
  3. }
  4. foo.add = (left: number, right: number) => {
  5. return left + right
  6. }
  7. foo.sub = (left: number, right: number) => {
  8. return left - right
  9. }

建议改法

  1. class Foo {
  2. static foo(value: number): void {
  3. console.log(value.toString())
  4. }
  5. static add(left: number, right: number): number {
  6. return left + right
  7. }
  8. static sub(left: number, right: number): number {
  9. return left - right
  10. }
  11. }

状态管理使用典型场景

Struct组件外使用状态变量

由于struct和class不同,不建议把this作为参数传递到struct外部使用,避免引起实例引用无法释放的情况,导致内存泄露。建议将状态变量对象传递到struct外面使用,通过修改对象的属性,来触发UI刷新。

不推荐用法

  1. export class MyComponentController {
  2. item: MyComponent = null;
  3. setItem(item: MyComponent) {
  4. this.item = item;
  5. }
  6. changeText(value: string) {
  7. this.item.value = value;
  8. }
  9. }
  10. @Component
  11. export default struct MyComponent {
  12. public controller: MyComponentController = null;
  13. @State value: string = 'Hello World';
  14. build() {
  15. Column() {
  16. Text(this.value)
  17. .fontSize(50)
  18. }
  19. }
  20. aboutToAppear() {
  21. if (this.controller)
  22. this.controller.setItem(this);
  23. }
  24. }
  25. @Entry
  26. @Component
  27. struct ObjThisOldPage {
  28. controller = new MyComponentController();
  29. build() {
  30. Column() {
  31. MyComponent({ controller: this.controller })
  32. Button('change value').onClick(() => {
  33. this.controller.changeText('Text');
  34. })
  35. }
  36. }
  37. }

推荐用法

  1. class CC {
  2. value: string = '1';
  3. constructor(value: string) {
  4. this.value = value;
  5. }
  6. }
  7. export class MyComponentController {
  8. item: CC = new CC('1');
  9. setItem(item: CC) {
  10. this.item = item;
  11. }
  12. changeText(value: string) {
  13. this.item.value = value;
  14. }
  15. }
  16. @Component
  17. export default struct MyComponent {
  18. public controller: MyComponentController | null = null;
  19. @State value: CC = new CC('Hello World')
  20. build() {
  21. Column() {
  22. Text(`${this.value.value}`)
  23. .fontSize(50)
  24. }
  25. }
  26. aboutToAppear() {
  27. if (this.controller)
  28. this.controller.setItem(this.value);
  29. }
  30. }
  31. @Entry
  32. @Component
  33. struct StyleExample {
  34. controller: MyComponentController = new MyComponentController();
  35. build() {
  36. Column() {
  37. MyComponent({ controller: this.controller })
  38. Button('change value').onClick(() => {
  39. this.controller.changeText('Text')
  40. })
  41. }
  42. }
  43. }

Struct支持联合类型的方案

下面这段代码有arkts-no-any-unknown的报错,由于strcut不支持泛型,建议使用联合类型,实现自定义组件类似泛型的功能。

不推荐用法

  1. class Data {
  2. aa: number = 11;
  3. }
  4. @Entry
  5. @Component
  6. struct DatauionOldPage {
  7. @State array: Data[] = [new Data(), new Data(), new Data()];
  8. @Builder
  9. componentCloser(data: Data) {
  10. Text(data.aa + '').fontSize(50)
  11. }
  12. build() {
  13. Row() {
  14. Column() {
  15. ForEachCom({ arrayList: this.array, closer: this.componentCloser })
  16. }
  17. .width('100%')
  18. }
  19. .height('100%')
  20. }
  21. }
  22. @Component
  23. export struct ForEachCom {
  24. arrayList: any[]
  25. @BuilderParam closer: (data: any) => void = this.componentCloser
  26. @Builder
  27. componentCloser() {
  28. }
  29. build() {
  30. Column() {
  31. ForEach(this.arrayList, (item: any) => {
  32. Row() {
  33. this.closer(item)
  34. }.width('100%').height(200).backgroundColor('#eee')
  35. })
  36. }
  37. }
  38. }

推荐用法

  1. class Data {
  2. aa: number = 11;
  3. }
  4. class Model {
  5. aa: string = '11';
  6. }
  7. type UnionData = Data | Model
  8. @Entry
  9. @Component
  10. struct DatauionPage {
  11. array: UnionData[] = [new Data(), new Data(), new Data()];
  12. @Builder
  13. componentCloser(data: UnionData) {
  14. if (data instanceof Data) {
  15. Text(data.aa + '').fontSize(50)
  16. }
  17. }
  18. build() {
  19. Row() {
  20. Column() {
  21. ForEachCom({ arrayList: this.array, closer: this.componentCloser })
  22. }
  23. .width('100%')
  24. }
  25. .height('100%')
  26. }
  27. }
  28. @Component
  29. export struct ForEachCom {
  30. arrayList: UnionData[] = [new Data(), new Data(), new Data()];
  31. @BuilderParam closer: (data: UnionData) => void = this.componentCloser
  32. @Builder
  33. componentCloser() {
  34. }
  35. build() {
  36. Column() {
  37. ForEach(this.arrayList, (item: UnionData) => {
  38. Row() {
  39. this.closer(item)
  40. }.width('100%').height(200).backgroundColor('#eee')
  41. })
  42. }
  43. }
  44. }

最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!

获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

鸿蒙(HarmonyOS NEXT)最新学习路线

  •  HarmonOS基础技能

  • HarmonOS就业必备技能 
  •  HarmonOS多媒体技术

  • 鸿蒙NaPi组件进阶

  • HarmonOS高级技能

  • 初识HarmonOS内核 
  • 实战就业级设备开发

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

《鸿蒙 (OpenHarmony)开发入门教学视频》

《鸿蒙生态应用开发V2.0白皮书》

图片

《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

图片

 《鸿蒙开发基础》

  • ArkTS语言
  • 安装DevEco Studio
  • 运用你的第一个ArkTS应用
  • ArkUI声明式UI开发
  • .……

图片

 《鸿蒙开发进阶》

  • Stage模型入门
  • 网络管理
  • 数据管理
  • 电话服务
  • 分布式应用开发
  • 通知与窗口管理
  • 多媒体技术
  • 安全技能
  • 任务管理
  • WebGL
  • 国际化开发
  • 应用测试
  • DFX面向未来设计
  • 鸿蒙系统移植和裁剪定制
  • ……

图片

《鸿蒙进阶实战》

  • ArkTS实践
  • UIAbility应用
  • 网络案例
  • ……

图片

 获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结

总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/运维做开发/article/detail/989543
推荐阅读
相关标签