Skip to content

Commit c1c7ebf

Browse files
committed
TS: Support const type assertions
1 parent d5ae69d commit c1c7ebf

File tree

9 files changed

+51
-9
lines changed

9 files changed

+51
-9
lines changed

javascript/extractor/src/com/semmle/js/parser/TypeScriptASTConverter.java

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ private Node convertNodeUntyped(JsonObject node, String defaultKind) throws Pars
345345
case "ArrowFunction":
346346
return convertArrowFunction(node, loc);
347347
case "AsExpression":
348-
return convertAsExpression(node, loc);
348+
return convertTypeAssertionExpression(node, loc);
349349
case "AwaitExpression":
350350
return convertAwaitExpression(node, loc);
351351
case "BigIntKeyword":
@@ -797,11 +797,6 @@ private Node convertArrowFunction(JsonObject node, SourceLocation loc) throws Pa
797797
convertChildAsType(node, "type"));
798798
}
799799

800-
private Node convertAsExpression(JsonObject node, SourceLocation loc) throws ParseError {
801-
return new TypeAssertion(
802-
loc, convertChild(node, "expression"), convertChildAsType(node, "type"), true);
803-
}
804-
805800
private Node convertAwaitExpression(JsonObject node, SourceLocation loc) throws ParseError {
806801
return new AwaitExpression(loc, convertChild(node, "expression"));
807802
}
@@ -2115,8 +2110,12 @@ private Node convertTypeAliasDeclaration(JsonObject node, SourceLocation loc) th
21152110

21162111
private Node convertTypeAssertionExpression(JsonObject node, SourceLocation loc)
21172112
throws ParseError {
2118-
return new TypeAssertion(
2119-
loc, convertChild(node, "expression"), convertChildAsType(node, "type"), false);
2113+
ITypeExpression type = convertChildAsType(node, "type");
2114+
// `T as const` is extracted as a cast to the keyword type `const`.
2115+
if (type instanceof Identifier && ((Identifier) type).getName().equals("const")) {
2116+
type = new KeywordTypeExpr(type.getLoc(), "const");
2117+
}
2118+
return new TypeAssertion(loc, convertChild(node, "expression"), type, false);
21202119
}
21212120

21222121
private Node convertTypeLiteral(JsonObject obj, SourceLocation loc) throws ParseError {

javascript/ql/src/semmle/javascript/TypeScript.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,9 @@ class TypeExpr extends ExprOrType, @typeexpr {
585585
/** Holds if this is the `bigint` type. */
586586
predicate isBigInt() { none() }
587587

588+
/** Holds if this is the `const` keyword, occurding in a type assertion such as `x as const`. */
589+
predicate isConstKeyword() { none() }
590+
588591
/** Gets this type expression, with any surrounding parentheses removed. */
589592
override TypeExpr stripParens() { result = this }
590593

@@ -638,6 +641,8 @@ private class KeywordTypeExpr extends @keywordtypeexpr, TypeExpr {
638641
override predicate isUnknownKeyword() { getName() = "unknown" }
639642

640643
override predicate isBigInt() { getName() = "bigint" }
644+
645+
override predicate isConstKeyword() { getName() = "const" }
641646
}
642647

643648
/**
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
test_ConstKeyword
2+
| tst.ts:1:19:1:23 | const |
3+
| tst.ts:2:10:2:14 | const |
4+
test_ConstTypeAssertion
5+
| tst.ts:1:9:1:23 | [1, 2] as const |
6+
| tst.ts:2:9:2:21 | <const>[1, 2] |
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import javascript
2+
3+
query predicate test_ConstKeyword(TypeExpr t) {
4+
t.isConstKeyword()
5+
}
6+
7+
query predicate test_ConstTypeAssertion(TypeAssertion t) {
8+
t.getTypeAnnotation().isConstKeyword()
9+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
var x = [1, 2] as const;
2+
var x = <const>[1, 2];

javascript/ql/test/library-tests/TypeScript/Types/GetExprType.expected

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@
8080
| tst.ts:38:5:38:24 | tupleWithRestElement | [number, ...string[]] |
8181
| tst.ts:39:5:39:36 | tupleWi ... lements | [number, string?, ...number[]] |
8282
| tst.ts:40:5:40:15 | unknownType | unknown |
83+
| tst.ts:42:5:42:21 | constArrayLiteral | readonly [1, 2] |
84+
| tst.ts:42:25:42:30 | [1, 2] | readonly [1, 2] |
85+
| tst.ts:42:25:42:39 | [1, 2] as const | readonly [1, 2] |
86+
| tst.ts:42:26:42:26 | 1 | 1 |
87+
| tst.ts:42:29:42:29 | 2 | 2 |
88+
| tst.ts:43:5:43:22 | constObjectLiteral | { readonly foo: "foo"; } |
89+
| tst.ts:43:26:43:39 | { foo: "foo" } | { readonly foo: "foo"; } |
90+
| tst.ts:43:26:43:48 | { foo: ... s const | { readonly foo: "foo"; } |
91+
| tst.ts:43:28:43:30 | foo | "foo" |
92+
| tst.ts:43:33:43:37 | "foo" | "foo" |
8393
| type_alias.ts:3:5:3:5 | b | boolean |
8494
| type_definition_objects.ts:1:13:1:17 | dummy | typeof dummy.ts |
8595
| type_definition_objects.ts:1:24:1:32 | "./dummy" | any |

javascript/ql/test/library-tests/TypeScript/Types/GetTypeExprType.expected

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@
6868
| tst.ts:39:60:39:65 | number | number |
6969
| tst.ts:39:60:39:67 | number[] | number[] |
7070
| tst.ts:40:18:40:24 | unknown | unknown |
71+
| tst.ts:42:35:42:39 | const | any |
72+
| tst.ts:43:44:43:48 | const | any |
7173
| type_alias.ts:1:6:1:6 | B | boolean |
7274
| type_alias.ts:1:10:1:16 | boolean | boolean |
7375
| type_alias.ts:3:8:3:8 | B | boolean |

javascript/ql/test/library-tests/TypeScript/Types/TupleTypes.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,9 @@
88
| tst.ts:39:5:39:36 | tupleWi ... lements | [number, string?, ...number[]] | 0 | number | 1 | number |
99
| tst.ts:39:5:39:36 | tupleWi ... lements | [number, string?, ...number[]] | 1 | string | 1 | number |
1010
| tst.ts:39:5:39:36 | tupleWi ... lements | [number, string?, ...number[]] | 2 | number | 1 | number |
11+
| tst.ts:42:5:42:21 | constArrayLiteral | readonly [1, 2] | 0 | 1 | 2 | no-rest |
12+
| tst.ts:42:5:42:21 | constArrayLiteral | readonly [1, 2] | 1 | 2 | 2 | no-rest |
13+
| tst.ts:42:25:42:30 | [1, 2] | readonly [1, 2] | 0 | 1 | 2 | no-rest |
14+
| tst.ts:42:25:42:30 | [1, 2] | readonly [1, 2] | 1 | 2 | 2 | no-rest |
15+
| tst.ts:42:25:42:39 | [1, 2] as const | readonly [1, 2] | 0 | 1 | 2 | no-rest |
16+
| tst.ts:42:25:42:39 | [1, 2] as const | readonly [1, 2] | 1 | 2 | 2 | no-rest |

javascript/ql/test/library-tests/TypeScript/Types/tst.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,7 @@ let tupleWithOptionalElement: [number, string, number?];
3737
let emptyTuple: [];
3838
let tupleWithRestElement: [number, ...string[]];
3939
let tupleWithOptionalAndRestElements: [number, string?, ...number[]];
40-
let unknownType: unknown;
40+
let unknownType: unknown;
41+
42+
let constArrayLiteral = [1, 2] as const;
43+
let constObjectLiteral = { foo: "foo" } as const;

0 commit comments

Comments
 (0)