赞
踩
本文通过更多应用场景中的案例,提供在ArkTS语法规则下将TS代码适配成ArkTS代码的建议。各章以ArkTS语法规则英文名称命名,每个案例提供适配前的TS代码和适配后的ArkTS代码。
应用代码
- interface W {
- bundleName: string
- action: string
- entities: string[]
- }
-
- let wantInfo: W = {
- 'bundleName': 'com.huawei.hmos.browser',
- 'action': 'ohos.want.action.viewData',
- 'entities': ['entity.system.browsable']
- }
建议改法
- interface W {
- bundleName: string
- action: string
- entities: string[]
- }
-
- let wantInfo: W = {
- bundleName: 'com.huawei.hmos.browser',
- action: 'ohos.want.action.viewData',
- entities: ['entity.system.browsable']
- }
- function printObj(obj: any) {
- console.log(obj)
- }
-
- printObj('abc')
建议改法
- function printObj(obj: string) {
- console.log(obj)
- }
-
- printObj('abc')
应用代码
- class A {
- v: number = 0
- s: string = ''
-
- foo(str: string) {
- let tmpStr = JSON.parse(str)
- if (tmpStr.add != undefined) {
- this.v = tmpStr.v
- this.s = tmpStr.s
- }
- }
- }
建议改法
- class A {
- v: number = 0
- s: string = ''
-
- foo(str: string) {
- let tmpStr: Record<string, Object> = JSON.parse(str)
- if (tmpStr.add != undefined) {
- this.v = tmpStr.v as number
- this.s = tmpStr.s as string
- }
- }
- }
应用代码
- function printProperties(obj: any) {
- console.log(obj.name)
- console.log(obj.value)
- }
建议改法
- function printProperties(obj: Record<string, Object>) {
- console.log(obj.name)
- console.log(obj.value)
- }
使用函数类型来替代。
应用代码
- interface I {
- (value: string): void;
- }
-
- function foo(fn: I) {
- fn('abc')
- }
- foo((value: string) => {
- console.log(value)
- })
建议改法
- type I = (value: string) => void
-
- function foo(fn: I) {
- fn('abc')
- }
- foo((value: string) => {
- console.log(value)
- })
应用代码
- class Controller {
- value: number = 0
-
- constructor(value: number) {
- this.value = value
- }
- }
-
- type ControllerConstrucotr = {
- new (value: number): Controller;
- }
-
- class Menu {
- controller: ControllerConstrucotr = Controller
- createController() {
- if (this.controller) {
- return new this.controller(123)
- }
- return null;
- }
- }
-
- let t = new Menu()
- console.log(t.createController()!.value)
建议改法
- class Controller {
- value: number = 0
-
- constructor(value: number) {
- this.value = value
- }
- }
-
- type ControllerConstrucotr = () => Controller;
-
- class Menu {
- controller: ControllerConstrucotr = () => {
- return new Controller(123)
- }
-
- createController() {
- if (this.controller) {
- return this.controller()
- }
- return null;
- }
- }
-
- let t: Menu = new Menu()
- console.log(t.createController()!.value)
使用Record类型来替代。
应用代码
- function foo(data: { [key: string]: string }) {
- data['a'] = 'a'
- data['b'] = 'b'
- data['c'] = 'c'
- }
建议改法
- function foo(data: Record<string, string>) {
- data['a'] = 'a'
- data['b'] = 'b'
- data['c'] = 'c'
- }
应用代码
- class C {
- getInstance(): this {
- return this
- }
- }
建议改法
- class C {
- getInstance(): C {
- return this
- }
- }
应用代码
- class Person {
- constructor(readonly name: string) {}
-
- getName(): string {
- return this.name
- }
- }
建议改法
- class Person {
- name: string
- constructor(name: string) {
- this.name = name
- }
-
- getName(): string {
- return this.name
- }
- }
应用代码
- class Controller {
- value: number = 0
-
- constructor(value: number) {
- this.value = value
- }
- }
-
- interface ControllerConstrucotr {
- new (value: number): Controller;
- }
-
- class Menu {
- controller: ControllerConstrucotr = Controller
- createController() {
- if (this.controller) {
- return new this.controller(123)
- }
- return null;
- }
- }
-
- let t = new Menu()
- console.log(t.createController()!.value)
建议改法
- class Controller {
- value: number = 0
-
- constructor(value: number) {
- this.value = value
- }
- }
-
- type ControllerConstrucotr = () => Controller;
-
- class Menu {
- controller: ControllerConstrucotr = () => {
- return new Controller(123)
- }
-
- createController() {
- if (this.controller) {
- return this.controller()
- }
- return null;
- }
- }
-
- let t: Menu = new Menu()
- console.log(t.createController()!.value)
可以转换成Record类型,用来访问对象的属性。
应用代码
- import myRouter from '@ohos.router';
- let params: Object = myRouter.getParams();
- let funNum: number = params['funNum'];
- let target: string = params['target'];
建议改法
- import myRouter from '@ohos.router';
- let params = myRouter.getParams() as Record<string, string | number>;
- let funNum: number = params.funNum as number;
- let target: string = params.target as string;
应用代码
- class A {
- str: string = ''
- }
- class B extends A {}
- class C extends A {}
-
- let arr: Array<A> = []
-
- let originMenusMap:Map<string, C> = new Map(arr.map(item => [item.str, (item instanceof C) ? item : null]))
建议改法
- class A {
- str: string = ''
- }
- class B extends A {}
- class C extends A {}
-
- let arr: Array<A> = []
-
- 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 | null
map
应用代码
let regex: RegExp = /\s*/g
建议改法
let regexp: RegExp = new RegExp('\\s*','g')
原因
如果正则表达式中使用了标志符,需要将其作为的参数。new RegExp()
应用代码
- const area = {
- pixels: new ArrayBuffer(8),
- offset: 0,
- stride: 8,
- region: { size: { height: 1,width:2 }, x: 0, y: 0 }
- }
建议改法
- import image from '@ohos.multimedia.image';
-
- const area: image.PositionArea = {
- pixels: new ArrayBuffer(8),
- offset: 0,
- stride: 8,
- region: { size: { height: 1, width: 2 }, x: 0, y: 0 }
- }
应用代码
- class Test {
- value: number = 1
-
- constructor(value: number) {
- this.value = value
- }
- }
-
- let t: Test = { value: 2 }
建议改法1
- // 去除构造函数
- class Test {
- value: number = 1
- }
-
- let t: Test = { value: 2 }
建议改法2
- // 使用new
- class Test {
- value: number = 1
-
- constructor(value: number) {
- this.value = value
- }
- }
-
- let t: Test = new Test(2)
原因
- class C {
- value: number = 1
-
- constructor(n: number) {
- if (n < 0) {
- throw new Error('Negative')
- }
- this.value = n
- }
- }
-
- let s: C = new C(-2) //抛出异常
- let t: C = { value : -2 } //ArkTS不支持
例如在上面的例子中,如果允许使用来标注object literal的类型,那么上述代码中的变量会导致行为的二义性。ArkTS禁止通过object literal来绕过这一行为。C
t
应用代码
- class Test {
- value: number = 0
- }
-
- let arr: Test[] = [
- {
- 'value': 1
- },
- {
- 'value': 2
- },
- {
- 'value': 3
- }
- ]
建议改法
- class Test {
- value: number = 0
- }
- let arr: Test[] = [
- {
- value: 1
- },
- {
- value: 2
- },
- {
- value: 3
- }
- ]
应用代码
- let obj: Record<string, number | string> = {
- value: 123,
- name: 'abc'
- }
建议改法
- let obj: Record<string, number | string> = {
- 'value': 123,
- 'name': 'abc'
- }
应用代码
- function foo(obj: { [key: string]: string}): string {
- if (obj != undefined && obj != null) {
- return obj.value1 + obj.value2
- }
- return ''
- }
建议改法
- function foo(obj: Record<string, string>): string {
- if (obj != undefined && obj != null) {
- return obj.value1 + obj.value2
- }
- return ''
- }
应用代码
- (fn) => {
- fn({ value: 123, name:'' })
- }
建议改法
- class T {
- value: number = 0
- name: string = ''
- }
-
- (fn: (v: T) => void) => {
- fn({ value: 123, name: '' })
- }
应用代码
- interface T {
- foo(value: number): number
- }
-
- let t: T = { foo: (value) => { return value } }
建议改法1
- interface T {
- foo: (value: number) => number
- }
-
- let t:T = { foo: (value) => { return value } }
建议改法2
- class T {
- foo: (value: number) => number = (value: number) => {
- return value
- }
- }
-
- let t:T = new T()
原因
class/interface中声明的方法应该被所有class的实例共享。ArkTS不支持通过object literal改写实例方法。ArkTS支持函数类型的属性。
应用代码
- import hilog from '@ohos.hilog'
-
- export default {
- onCreate() {
- hilog.info(0x0000, 'testTag', '%{public}s', 'Application onCreate');
- },
- onDestroy() {
- hilog.info(0x0000, 'testTag', '%{public}s', 'Application onDestroy')
- }
- }
建议改法
- import hilog from '@ohos.hilog'
-
- class Test {
- onCreate() {
- hilog.info(0x0000, 'testTag', '%{public}s', 'Application onCreate');
- }
- onDestroy() {
- hilog.info(0x0000, 'testTag', '%{public}s', 'Application onDestroy')
- }
- }
-
- export default new Test()
应用代码
- import { BusinessError } from '@ohos.base';
- import bluetooth from '@ohos.bluetooth';
- let serverNumber = -1;
- function serverSocket(code: BusinessError, num: number) {
- console.log('bluetooth error code: ' + code.code);
- if (code.code == 0) {
- console.log('bluetooth serverSocket Number: ' + num);
- serverNumber = num;
- }
- }
-
- let sppOption = { uuid: '', secure: false, type: 0 };
- bluetooth.sppListen('', sppOption, serverSocket);
建议改法
- import { BusinessError } from '@ohos.base';
- import bluetooth from '@ohos.bluetooth';
- let serverNumber = -1;
- function serverSocket(code: BusinessError, num: number) {
- console.log('bluetooth error code: ' + code.code);
- if (code.code == 0) {
- console.log('bluetooth serverSocket Number: ' + num);
- serverNumber = num;
- }
- }
-
- let sppOption: bluetooth.SppOption = { uuid: '', secure: false, type: 0 };
- bluetooth.sppListen('', sppOption, serverSocket);
原因
对象字面量缺少类型,根据分析可以得知,的类型来源于SDK,那么只需要将类型导入即可。 注意到在中,是定义在namespace中的,所以在ets文件中,先导入namespace,再通过名称获取相应的类型。bluetooth.sppListen
sppOption
@ohos.bluetooth
sppOption
应用代码
- function emit(event: string, ...args: Object[]): void {}
-
- emit('', {
- 'action': 11,
- 'outers': false
- })
建议改法
- function emit(event: string, ...args: Object[]): void {}
-
- let emitArg: Record<string, number | boolean> = {
- 'action': 11,
- 'outers': false
- }
-
- emit('', emitArg)
应用代码
type Person = { name: string, age: number }
建议改法
- interface Person {
- name: string,
- age: number
- }
应用代码
- let permissionList = [
- { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' },
- { name: '麦克风', value: '用于反馈问题单时增加语音' },
- { name: '存储', value: '用于反馈问题单时增加本地文件附件' }
- ]
建议改法
为对象字面量声明类型
- class PermissionItem {
- name?: string
- value?: string
- }
-
- let permissionList: PermissionItem[] = [
- { name: '设备信息', value: '用于分析设备的续航、通话、上网、SIM卡故障等' },
- { name: '麦克风', value: '用于反馈问题单时增加语音' },
- { name: '存储', value: '用于反馈问题单时增加本地文件附件' }
- ]
应用代码
- class C {
- add(left: number, right: number): number {
- return left + right;
- }
- }
-
- function sub(left: number, right: number): number {
- return left - right;
- }
-
- let c1 = new C()
- c1.add = sub
建议改法
- class C {
- add: (left: number, right: number) => number =
- (left: number, right: number) => {
- return left + right;
- }
- }
-
- function sub(left: number, right: number): number {
- return left - right;
- }
-
- let c1 = new C()
- c1.add = sub
应用代码
- let a = +'5'
- let b = -'5'
- let c = ~'5'
- let d = +'string'
建议改法
- let a = Number.parseInt('5')
- let b = -Number.parseInt('5')
- let c = ~Number.parseInt('5')
- let d = new Number('string')
应用代码
- // module1.ts
- class C {
- value: number = 0
- }
-
- export let c = new C()
-
- // module2.ts
- import { c } from './module1'
- let t: typeof c = { value: 123 }
建议改法
- // module1.ts
- class C {
- value: number = 0
- }
-
- export { C }
-
- // module2.ts
- import { C } from './module1'
- let t: C = { value: 123 }
应用代码
- let arr = [10, 20, 30, 40]
- let isIn = 5 in arr
建议改法
- let arr = [10, 20, 30, 40]
- let isIn = 5 < arr.length
应用代码
- let map = new Map<string, string>([['a', 'a'], ['b', 'b']])
- for (let [key, value] of map) {
- console.log(key)
- console.log(value)
- }
建议改法
使用数组
- let map = new Map<string, string>([['a', 'a'], ['b', 'b']])
- for (let arr of map) {
- let key = arr[0]
- let value = arr[1]
- console.log(key)
- console.log(value)
- }
应用代码
- import { BusinessError } from '@ohos.base';
-
- try {
- // ...
- } catch (e: BusinessError) {
- logger.error(e.code, e.message);
- }
建议改法
- import { BusinessError } from '@ohos.base';
-
- try {
- // ...
- } catch (error) {
- let e: BusinessError = error as BusinessError;
- logger.error(e.code, e.message);
- }
应用代码
- interface Person {
- [name: string]: string
- }
- let p: Person = {
- name: 'tom',
- age: '18'
- };
-
- for (let t in p) {
- console.log(p[t])
- }
建议改法
- let p: Record<string, string> = {
- 'name': 'tom',
- 'age': '18'
- };
-
- for (let ele of Object.entries(p)) {
- console.log(ele[1])
- }
应用代码
- class C {
- a: number = 0
- b: number = 0
- c: number = 0
- }
- type OptionsFlags = {
- [Property in keyof C]: string
- }
建议改法
- class C {
- a: number = 0
- b: number = 0
- c: number = 0
- }
-
- type OptionsFlags = Record<keyof C, string>
应用代码
- import { BusinessError } from '@ohos.base';
-
- function ThrowError(error: BusinessError) {
- throw error
- }
建议改法
- import { BusinessError } from '@ohos.base';
-
- function ThrowError(error: BusinessError) {
- throw error as Error
- }
原因
throw
语句中值的类型必须为或者其继承类,如果继承类是一个泛型,会有编译期报错。建议使用将类型转换为。Error
as
Error
应用代码
- function foo() {
- console.log(this.value)
- }
-
- let obj = { value: 123 }
- foo.apply(obj)
建议改法1
使用类的方法实现,如果该方法被多个类使用,可以考虑采用继承的机制
- class Test {
- value: number = 0
- constructor (value: number) {
- this.value = value
- }
-
- foo() {
- console.log(this.value)
- }
- }
-
- let obj: Test = new Test(123)
- obj.foo()
建议改法2
将this作为参数传入
- function foo(obj: Test) {
- console.log(obj.value)
- }
-
- class Test {
- value: number = 0
- }
-
- let obj: Test = { value: 123 }
- foo(obj)
建议改法3
将属性作为参数传入
- function foo(value: number) {
- console.log(value)
- }
-
- class Test {
- value: number = 0
- }
-
- let obj: Test = { value: 123 }
- foo(obj.value)
应用代码
- class Test {
- static value: number = 123
- static foo(): number {
- return this.value
- }
- }
建议改法
- class Test {
- static value: number = 123
- static foo(): number {
- return Test.value
- }
- }
应用代码
- import notification from '@ohos.notificationManager';
-
- function buildNotifyLongRequest(): notification.NotificationRequest {
- // ...
- }
-
- let notificationRequest: notification.NotificationRequest = {
- ...buildNotifyLongRequest(),
- deliveryTime: new Date().getTime()
- }
建议改法
- import notification from '@ohos.notificationManager';
-
- function buildNotifyLongRequest():notification.NotificationRequest {
- // ...
- }
-
- let notificationRequest: notification.NotificationRequest = buildNotifyLongRequest();
- notificationRequest.deliveryTime = new Date().getTime();
原因
ArkTS中,对象布局在编译期是确定的。如果需要将一个对象的所有属性展开赋值给另一个对象可以通过逐个属性赋值语句完成。在本例中,需要展开的对象和赋值的目标对象类型恰好相同,可以通过改变该对象属性的方式重构代码。
在class内声明属性,而不是在构造函数上。
应用代码
- class Controller {
- value: number = 0
- constructor(value: number) {
- this.value = value
- }
- }
-
- type ControllerConstrucotr = new (value: number) => Controller;
-
- class Menu {
- controller: ControllerConstrucotr = Controller
- createController() {
- if (this.controller) {
- return new this.controller(123)
- }
- return null;
- }
- }
-
- let t = new Menu()
- console.log(t.createController()!.value)
建议改法
- class Controller {
- value: number = 0
- constructor(value: number) {
- this.value = value
- }
- }
-
- type ControllerConstrucotr = () => Controller;
-
- class Menu {
- controller: ControllerConstrucotr = () => { return new Controller(123) }
- createController() {
- if (this.controller) {
- return this.controller()
- }
- return null;
- }
- }
-
- let t: Menu = new Menu()
- console.log(t.createController()!.value)
由于无法为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
-
- print(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'
-
- console.log(GlobalContext.getContext().getObject('value'));
应用代码
- 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))
应用代码
- class A {
- value: number = 0
- foo: Function = () => {}
- }
-
- class Test {
- value: number = 1234
- obj: A = {
- value: this.value,
- foo: this.foo.bind(this)
- }
-
- foo() {
- console.log(this.value)
- }
- }
建议改法1
- class A {
- value: number = 0
- foo: Function = () => {}
- }
-
- class Test {
- value: number = 1234
- obj: A = {
- value: this.value,
- foo: (): void => this.foo()
- }
-
- foo() {
- console.log(this.value)
- }
- }
建议改法2
- class A {
- value: number = 0
- foo: Function = () => {}
- }
-
- class Test {
- value: number = 1234
- foo: () => void = () => {
- console.log(this.value)
- }
- obj: A = {
- value: this.value,
- foo: this.foo
- }
- }
应用代码
- class A {
- value:number;
- constructor (value:number) {
- 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:number;
- constructor (value:number) {
- 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不允许使用全局对象的属性和方法: Infinity, NaN, isFinite, isNaN, parseFloat, parseInt
可以使用的属性和方法: Number
Infinity, NaN, isFinite, isNaN, parseFloat, parseInt
应用代码
- console.log(NaN)
- console.log(isFinite(123))
- console.log(parseInt('123'))
建议改法
- console.log(Number.NaN)
- console.log(Number.isFinite(123))
- console.log(Number.parseInt('123'))
应用代码
- 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;
- }
- }
应用代码
- 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 error
- class Test {
- value: nameber
- 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) => {}, '')
原因
例如,在以下的例子中,如果编译期不开启严格函数类型的检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。具体来看,在的函数体中,一个被传入(这是可以的,因为可以接受),但是在代码第6行的调用点,传入的的函数实现中,始终将参数当做string类型,允许其调用方法。如果不开启严格函数类型的检查,那么这段代码在运行时,会出现在上无法找到属性的错误。foo
undefined
fn
fn
undefined
foo
(value: string) => { console.log(value.toUpperCase()) }
value
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()
原因
在第一段代码中,如果编译期不开启严格空值检查,那么该段代码可以编译通过,但是在运行时会产生非预期的行为。这是因为的属性为(这是因为是的语法糖),在第11行调用方法时,由于在该方法体内未对的值进行空值检查,而直接按照类型访问其属性,这就导致了运行时的错误。为了避免运行时的非预期行为,如果在编译时开起来严格空值检查,这段代码将编译不通过从而可以提醒开发者修改代码(如按照第二段代码的方式),保证程序安全。t
value
undefined
value?: string
value : string | undefined = undefined
printValue
this.value
string
应用代码
- class Test {
- handleClick: (action: string, externInfo?: DiaExternInfo) => void | null = null;
- }
建议改法
在这种写法下,函数返回类型被解析为 ,需要添加括号用来区分union类型。void | undefined
- class Test {
- handleClick: ((action: string, externInfo?: DialogExternInfo) => void) | null = null;
- }
应用代码
- try {
-
- } catch (error) {
- console.log(error.message)
- }
建议改法
- import { BusinessError } from '@ohos.base'
-
- try {
-
- } catch (error) {
- console.log((error as BusinessError).message)
- }
应用代码
- class A {
- value: number
- constructor(value: number) {
- this.value = value
- }
- }
-
- function foo(v: number): A | null {
- if (v > 0) {
- return new A(v)
- }
- return null
- }
-
- let a: A = foo()
建议改法1
修改变量的类型:。a
let a: A | null = foo()
- class A {
- value: number
- constructor(value: number) {
- this.value = value
- }
- }
-
- function foo(v: number): A | null {
- if (v > 0) {
- return new A(v)
- }
- return null
- }
-
- let a: A | null = foo(123)
-
- if (a != null) {
- // 非空分支
- } else {
- // 处理null
- }
建议改法2
如果可以断定此处调用一定返回非空值,可以使用非空断言。foo
!
- class A {
- value: number
- constructor(value: number) {
- this.value = value
- }
- }
-
- function foo(v: number): A | null {
- if (v > 0) {
- return new A(v)
- }
- return null
- }
-
- let a: A = foo(123)!
应用代码
- interface A {
- foo?: () => void
- }
-
- let a:A = { foo: () => {} }
- a.foo()
建议改法1
- interface A {
- foo: () => void
- }
- let a: A = { foo: () => {} }
- a.foo()
建议改法2
- interface A {
- foo?: () => void
- }
-
- let a: A = { foo: () => {} }
- if (a.foo) {
- a.foo()
- }
原因
在原先代码的定义中,foo是可选属性,有可能为undefined,对undefined的调用会导致报错。建议按照业务逻辑判断是否需要为可选属性。如果确实需要,那么在访问到该属性后需要进行空值检查。
应用代码
- class Test {
- value: number = 0
- }
-
- let a: Test
- try {
- a = { value: 1}
- } catch (e) {
- a.value
- }
- a.value
建议改法
- class Test {
- value: number = 0
- }
-
- let a: Test | null = null
- try {
- a = { value:1 }
- } catch (e) {
- if (a) {
- a.value
- }
- }
-
- if (a) {
- a.value
- }
原因
对于primitive types,可以根据业务逻辑赋值,例如0,'',false。
对于对象类型,可以将类型修改为和null的联合类型,并赋值null,使用时需要进行非空检查。
应用代码
- function foo(a: number): number {
- if (a > 0) {
- return a
- }
- }
建议改法1
根据业务逻辑,在else分支中返回合适的数值
建议改法2
- function foo(a: number): number | undefined {
- if (a > 0) {
- return a
- }
- return
- }
应用代码
- // @ts-nocheck
- var a: any = 123
建议改法
let a: number = 123
原因
ArkTS不支持通过注释的方式绕过严格类型检查。首先将注释(或者)删去,再根据报错信息修改其他代码。// @ts-nocheck
// @ts-ignore
不允许.ts、.js文件.ets文件源码。import
建议改法
方式1.将.ts文件的后缀修改成ets,按照ArkTS语法规则适配代码。
方式2.将.ets文件中被.ts文件依赖的代码单独抽取到.ts文件中。
应用代码
import type {A, B, C, D } from '***'
建议改法
import {A, B, C, D } from '***'
应用代码
- class Controller {
- value: number = 0
- constructor(value: number) {
- this.value = value
- }
- }
-
- interface ControllerConstrucotr {
- new (value: number): Controller;
- }
-
- class Menu {
- controller: ControllerConstrucotr = Controller
- createController() {
- if (this.controller) {
- return new this.controller(123)
- }
- return null;
- }
- }
-
- let t = new Menu()
- console.log(t.createController()!.value)
建议改法
- class Controller {
- value: number = 0
- constructor(value: number) {
- this.value = value
- }
- }
-
- type ControllerConstrucotr = () => Controller;
-
- class Menu {
- controller: ControllerConstrucotr = () => { return new Controller(123) }
- createController() {
- if (this.controller) {
- return this.controller()
- }
- return null;
- }
- }
-
- let t: Menu = new Menu()
- console.log(t.createController()!.value)
应用代码
- class C1 {
- static value: string = 'abc'
- }
-
- class C2 {
- static value: string = 'def'
- }
-
- function getValue(obj: any) {
- return obj['value']
- }
-
- console.log(getValue(C1))
- console.log(getValue(C2))
建议改法
- class C1 {
- static value: string = 'abc'
- }
-
- class C2 {
- static value: string = 'def'
- }
-
- function getC1Value(): string {
- return C1.value;
- }
-
- function getC2Value(): string {
- return C2.value;
- }
-
- console.log(getC1Value())
- console.log(getC2Value())
改用动态import
应用代码
import 'module'
建议改法
import('module')
应用代码
- function foo(value: number): void {
- console.log(value.toString())
- }
-
- foo.add = (left: number, right: number) => {
- return left + right
- }
-
- foo.sub = (left: number, right: number) => {
- return left - right
- }
建议改法
- class Foo {
- static foo(value: number): void {
- console.log(value.toString())
- }
-
- static add(left: number, right: number): number {
- return left + right
- }
-
- static sub(left: number, right: number): number {
- return left - right
- }
- }
由于struct和class不同,不建议把this作为参数传递到struct外部使用,避免引起实例引用无法释放的情况,导致内存泄露。建议将状态变量对象传递到struct外面使用,通过修改对象的属性,来触发UI刷新。
不推荐用法
- export class MyComponentController {
- item: MyComponent = null;
-
- setItem(item: MyComponent) {
- this.item = item;
- }
-
- changeText(value: string) {
- this.item.value = value;
- }
- }
-
- @Component
- export default struct MyComponent {
- public controller: MyComponentController = null;
- @State value: string = 'Hello World';
-
- build() {
- Column() {
- Text(this.value)
- .fontSize(50)
- }
- }
-
- aboutToAppear() {
- if (this.controller)
- this.controller.setItem(this);
- }
- }
-
- @Entry
- @Component
- struct ObjThisOldPage {
- controller = new MyComponentController();
-
- build() {
- Column() {
- MyComponent({ controller: this.controller })
- Button('change value').onClick(() => {
- this.controller.changeText('Text');
- })
- }
- }
- }
推荐用法
- class CC {
- value: string = '1';
-
- constructor(value: string) {
- this.value = value;
- }
- }
-
- export class MyComponentController {
- item: CC = new CC('1');
-
- setItem(item: CC) {
- this.item = item;
- }
-
- changeText(value: string) {
- this.item.value = value;
- }
- }
-
- @Component
- export default struct MyComponent {
- public controller: MyComponentController | null = null;
- @State value: CC = new CC('Hello World')
-
- build() {
- Column() {
- Text(`${this.value.value}`)
- .fontSize(50)
- }
- }
-
- aboutToAppear() {
- if (this.controller)
- this.controller.setItem(this.value);
- }
- }
-
- @Entry
- @Component
- struct StyleExample {
- controller: MyComponentController = new MyComponentController();
-
- build() {
- Column() {
- MyComponent({ controller: this.controller })
- Button('change value').onClick(() => {
- this.controller.changeText('Text')
- })
- }
- }
- }
下面这段代码有arkts-no-any-unknown的报错,由于strcut不支持泛型,建议使用联合类型,实现自定义组件类似泛型的功能。
不推荐用法
- class Data {
- aa: number = 11;
- }
-
- @Entry
- @Component
- struct DatauionOldPage {
- @State array: Data[] = [new Data(), new Data(), new Data()];
-
- @Builder
- componentCloser(data: Data) {
- Text(data.aa + '').fontSize(50)
- }
-
- build() {
- Row() {
- Column() {
- ForEachCom({ arrayList: this.array, closer: this.componentCloser })
- }
- .width('100%')
- }
- .height('100%')
- }
- }
-
- @Component
- export struct ForEachCom {
- arrayList: any[]
- @BuilderParam closer: (data: any) => void = this.componentCloser
-
- @Builder
- componentCloser() {
- }
-
- build() {
- Column() {
- ForEach(this.arrayList, (item: any) => {
- Row() {
- this.closer(item)
- }.width('100%').height(200).backgroundColor('#eee')
- })
- }
- }
- }
推荐用法
- class Data {
- aa: number = 11;
- }
-
- class Model {
- aa: string = '11';
- }
-
- type UnionData = Data | Model
-
- @Entry
- @Component
- struct DatauionPage {
- array: UnionData[] = [new Data(), new Data(), new Data()];
-
- @Builder
- componentCloser(data: UnionData) {
- if (data instanceof Data) {
- Text(data.aa + '').fontSize(50)
- }
- }
-
- build() {
- Row() {
- Column() {
- ForEachCom({ arrayList: this.array, closer: this.componentCloser })
- }
- .width('100%')
- }
- .height('100%')
- }
- }
-
- @Component
- export struct ForEachCom {
- arrayList: UnionData[] = [new Data(), new Data(), new Data()];
- @BuilderParam closer: (data: UnionData) => void = this.componentCloser
-
- @Builder
- componentCloser() {
- }
-
- build() {
- Column() {
- ForEach(this.arrayList, (item: UnionData) => {
- Row() {
- this.closer(item)
- }.width('100%').height(200).backgroundColor('#eee')
- })
- }
- }
- }
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)技术知识点。
希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取,限时开源,先到先得~无套路领取!!
获取这份完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
HarmonOS基础技能
有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:ArkTS、ArkUI、Web开发、应用模型、资源分类…等知识点。
获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料
OpenHarmony北向、南向开发环境搭建
获取以上完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。