X Tutup
Skip to content

Commit 19db39c

Browse files
tomblindPerryvw
authored andcommitted
isNumberType union recursion (#692)
fixes #687 This updates `isNumberType` to match the behavior of `isStringType`, recursing into unions and intersections. This fixes issues where enum values were not seen as numeric types when used as array indexes.
1 parent 26d7338 commit 19db39c

File tree

3 files changed

+70
-18
lines changed

3 files changed

+70
-18
lines changed

src/LuaTransformer.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2505,7 +2505,11 @@ export class LuaTransformer {
25052505
);
25062506
}
25072507

2508-
if (statement.expression.arguments.some(a => !tsHelper.isNumberType(this.checker.getTypeAtLocation(a)))) {
2508+
if (
2509+
statement.expression.arguments.some(
2510+
a => !tsHelper.isNumberType(this.checker.getTypeAtLocation(a), this.checker, this.program)
2511+
)
2512+
) {
25092513
throw TSTLErrors.InvalidForRangeCall(statement.expression, "@forRange arguments must be number types.");
25102514
}
25112515

@@ -2522,7 +2526,7 @@ export class LuaTransformer {
25222526
}
25232527

25242528
const controlType = this.checker.getTypeAtLocation(controlDeclaration);
2525-
if (controlType && !tsHelper.isNumberType(controlType)) {
2529+
if (controlType && !tsHelper.isNumberType(controlType, this.checker, this.program)) {
25262530
throw TSTLErrors.InvalidForRangeCall(
25272531
statement.expression,
25282532
"@forRange function must return Iterable<number> or Array<number>."
@@ -3860,7 +3864,7 @@ export class LuaTransformer {
38603864
return this.transformLuaLibFunction(LuaLibFeature.Number, node, ...parameters);
38613865
case "isNaN":
38623866
case "isFinite":
3863-
const numberParameters = tsHelper.isNumberType(expressionType)
3867+
const numberParameters = tsHelper.isNumberType(expressionType, this.checker, this.program)
38643868
? parameters
38653869
: [this.transformLuaLibFunction(LuaLibFeature.Number, undefined, ...parameters)];
38663870

@@ -4295,7 +4299,10 @@ export class LuaTransformer {
42954299
const index = this.transformExpression(expression.argumentExpression);
42964300
const argumentType = this.checker.getTypeAtLocation(expression.argumentExpression);
42974301
const type = this.checker.getTypeAtLocation(expression.expression);
4298-
if (tsHelper.isNumberType(argumentType) && tsHelper.isArrayType(type, this.checker, this.program)) {
4302+
if (
4303+
tsHelper.isNumberType(argumentType, this.checker, this.program) &&
4304+
tsHelper.isArrayType(type, this.checker, this.program)
4305+
) {
42994306
return this.expressionPlusOne(index);
43004307
} else {
43014308
return index;
@@ -4315,7 +4322,10 @@ export class LuaTransformer {
43154322

43164323
const argumentType = this.checker.getTypeAtLocation(expression.argumentExpression);
43174324
const type = this.checker.getTypeAtLocation(expression.expression);
4318-
if (tsHelper.isNumberType(argumentType) && tsHelper.isStringType(type, this.checker, this.program)) {
4325+
if (
4326+
tsHelper.isNumberType(argumentType, this.checker, this.program) &&
4327+
tsHelper.isStringType(type, this.checker, this.program)
4328+
) {
43194329
const index = this.transformExpression(expression.argumentExpression);
43204330
return tstl.createCallExpression(
43214331
tstl.createTableIndexExpression(tstl.createIdentifier("string"), tstl.createStringLiteral("sub")),

src/TSHelper.ts

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -132,34 +132,45 @@ export function isStaticNode(node: ts.Node): boolean {
132132
return node.modifiers !== undefined && node.modifiers.some(m => m.kind === ts.SyntaxKind.StaticKeyword);
133133
}
134134

135-
export function isStringType(type: ts.Type, checker: ts.TypeChecker, program: ts.Program): boolean {
135+
export function isTypeWithFlags(
136+
type: ts.Type,
137+
flags: ts.TypeFlags,
138+
checker: ts.TypeChecker,
139+
program: ts.Program
140+
): boolean {
136141
if (type.symbol) {
137142
const baseConstraint = checker.getBaseConstraintOfType(type);
138143
if (baseConstraint && baseConstraint !== type) {
139-
return isStringType(baseConstraint, checker, program);
144+
return isTypeWithFlags(baseConstraint, flags, checker, program);
140145
}
141146
}
142147

143148
if (type.isUnion()) {
144-
return type.types.every(t => isStringType(t, checker, program));
149+
return type.types.every(t => isTypeWithFlags(t, flags, checker, program));
145150
}
146151

147152
if (type.isIntersection()) {
148-
return type.types.some(t => isStringType(t, checker, program));
153+
return type.types.some(t => isTypeWithFlags(t, flags, checker, program));
149154
}
150155

151-
return (
152-
(type.flags & ts.TypeFlags.String) !== 0 ||
153-
(type.flags & ts.TypeFlags.StringLike) !== 0 ||
154-
(type.flags & ts.TypeFlags.StringLiteral) !== 0
156+
return (type.flags & flags) !== 0;
157+
}
158+
159+
export function isStringType(type: ts.Type, checker: ts.TypeChecker, program: ts.Program): boolean {
160+
return isTypeWithFlags(
161+
type,
162+
ts.TypeFlags.String | ts.TypeFlags.StringLike | ts.TypeFlags.StringLiteral,
163+
checker,
164+
program
155165
);
156166
}
157167

158-
export function isNumberType(type: ts.Type): boolean {
159-
return (
160-
(type.flags & ts.TypeFlags.Number) !== 0 ||
161-
(type.flags & ts.TypeFlags.NumberLike) !== 0 ||
162-
(type.flags & ts.TypeFlags.NumberLiteral) !== 0
168+
export function isNumberType(type: ts.Type, checker: ts.TypeChecker, program: ts.Program): boolean {
169+
return isTypeWithFlags(
170+
type,
171+
ts.TypeFlags.Number | ts.TypeFlags.NumberLike | ts.TypeFlags.NumberLiteral,
172+
checker,
173+
program
163174
);
164175
}
165176

test/unit/enum.spec.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,3 +196,34 @@ test("enum concat", () => {
196196
return test + "_foobar";`;
197197
expect(util.transpileAndExecute(code)).toBe("0_foobar");
198198
});
199+
200+
test("enum value as array index", () => {
201+
const code = `
202+
enum TestEnum {
203+
A,
204+
B,
205+
C,
206+
}
207+
const arr = ["a", "b", "c"];
208+
let i = TestEnum.A;
209+
return arr[i];`;
210+
expect(util.transpileAndExecute(code)).toBe("a");
211+
});
212+
213+
test("enum property value as array index", () => {
214+
const code = `
215+
enum TestEnum {
216+
A,
217+
B,
218+
C,
219+
}
220+
221+
class Foo {
222+
i = TestEnum.A;
223+
}
224+
const foo = new Foo();
225+
226+
const arr = ["a", "b", "c"];
227+
return arr[foo.i];`;
228+
expect(util.transpileAndExecute(code)).toBe("a");
229+
});

0 commit comments

Comments
 (0)
X Tutup