18-ArkTs常见错误
arkts-no-untyped-obj-literals
用class为object literal标注类型,需要class的构造函数无参数
应用代码
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
// 使用newclass 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不支持
例如在上面的例子中,如果允许使用C来标注object literal的类型,那么上述代码中的变量t会导致行为的二义性。ArkTS禁止通过object literal来绕过这一行为。
用class/interface为object literal标注类型,需要使用identifier作为object literal的key
应用代码
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 }]
使用Record为object literal标注类型,需要使用字符串作为object literal的key
应用代码
let obj: Record<string, number | string> = { value: 123, name: 'abc'}
建议改法
let obj: Record<string, number | string> = { 'value': 123, 'name': 'abc'}
函数参数类型包含index signature
应用代码
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 '';}
函数实参使用了object literal
应用代码
(fn) => { fn({ value: 123, name:'' });}
建议改法
class T { value: number = 0 name: string = ''}
(fn: (v: T) => void) => { fn({ value: 123, name: '' });}
class/interface 中包含方法
应用代码
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支持函数类型的属性。
export default对象
应用代码
export default { onCreate() { // ... }, onDestroy() { // ... }}
建议改法
class Test { onCreate() { // ... } onDestroy() { // ... }}
export default new Test()
通过导入namespace获取类型
应用代码
// test.d.etsdeclare namespace test { interface I { id: string; type: number; }
function foo(name: string, option: I): void;}
export default test;
// app.etsimport { test } from 'test';
let option = { id: '', type: 0 };test.foo('', option);
建议改法
// test.d.etsdeclare namespace test { interface I { id: string; type: number; }
function foo(name: string, option: I): void;}
export default test;
// app.etsimport { test } from 'test';
let option: test.I = { id: '', type: 0 };test.foo('', option);
原因
对象字面量缺少类型,根据test.foo分析可以得知,option的类型来源于声明文件,那么只需要将类型导入即可。
注意到在test.d.ets中,I是定义在namespace中的,所以在ets文件中,先导入namespace,再通过名称获取相应的类型。
object literal传参给Object类型
应用代码
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);