Skip to content

Commit a4f1f56

Browse files
TheLartiansPerryvw
authored andcommitted
Detecting types derived from array (#289)
* check the base types to determine if a type inherits from Array<T> * dont block exceptions * remove empty else clause * remove space * added tsHelper.isDefaultArrayCallExpression() * added derived array recognition test code * Added isExplicitArrayType() method * added unit test * rewrite isDefaultArrayCall() -> isDefaultArrayCallMethodName() * change switch statement to Set<string> lookup
1 parent be2b164 commit a4f1f56

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

src/TSHelper.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
import * as ts from "typescript";
22
import { Decorator, DecoratorKind } from "./Decorator";
33

4+
const defaultArrayCallMethodNames = new Set<string>([
5+
"concat",
6+
"push",
7+
"reverse",
8+
"shift",
9+
"unshift",
10+
"sort",
11+
"pop",
12+
"forEach",
13+
"indexOf",
14+
"map",
15+
"filter",
16+
"some",
17+
"every",
18+
"slice",
19+
"splice",
20+
"join",
21+
]);
22+
423
export class TSHelper {
524

625
// Reverse lookup of enum key by value
@@ -74,11 +93,21 @@ export class TSHelper {
7493
&& (typeNode as ts.UnionOrIntersectionTypeNode).types.some(this.isArrayTypeNode));
7594
}
7695

77-
public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean {
96+
public static isExplicitArrayType(type: ts.Type, checker: ts.TypeChecker): boolean {
7897
const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias);
7998
return typeNode && this.isArrayTypeNode(typeNode);
8099
}
81100

101+
public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean {
102+
const baseTypes = type.getBaseTypes();
103+
if (baseTypes) {
104+
for (const baseType of baseTypes) {
105+
if (this.isExplicitArrayType(baseType, checker)) { return true; }
106+
}
107+
}
108+
return this.isExplicitArrayType(type, checker);
109+
}
110+
82111
public static isTupleReturnCall(node: ts.Node, checker: ts.TypeChecker): boolean {
83112
if (ts.isCallExpression(node)) {
84113
const type = checker.getTypeAtLocation(node.expression);
@@ -254,4 +283,9 @@ export class TSHelper {
254283
}
255284
return [false, null, null];
256285
}
286+
287+
public static isDefaultArrayCallMethodName(methodName: string): boolean {
288+
return defaultArrayCallMethodNames.has(methodName);
289+
}
290+
257291
}

src/Transpiler.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1203,7 +1203,14 @@ export abstract class LuaTranspiler {
12031203

12041204
}
12051205

1206-
if (tsHelper.isArrayType(ownerType, this.checker)) {
1206+
// if ownerType is a array, use only supported functions
1207+
if (tsHelper.isExplicitArrayType(ownerType, this.checker)) {
1208+
return this.transpileArrayCallExpression(node);
1209+
}
1210+
1211+
// if ownerType inherits from an array, use array calls where appropriate
1212+
if (tsHelper.isArrayType(ownerType, this.checker)
1213+
&& tsHelper.isDefaultArrayCallMethodName(this.transpileIdentifier(node.expression.name))) {
12071214
return this.transpileArrayCallExpression(node);
12081215
}
12091216

test/unit/array.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,17 @@ export class ArrayTests {
3838
const result = util.executeLua(lua);
3939
Expect(result).toBe(5);
4040
}
41+
42+
@Test("Derived array access")
43+
public derivedArrayAccess(): void {
44+
const lua = `local arr = {firstElement=function(self) return self[1]; end};`
45+
+ util.transpileString(
46+
`interface CustomArray extends Array<number>{ firstElement():number; };
47+
declare const arr: CustomArray;
48+
arr[0] = 3;
49+
return arr.firstElement();`
50+
);
51+
const result = util.executeLua(lua);
52+
Expect(result).toBe(3);
53+
}
4154
}

0 commit comments

Comments
 (0)