Skip to content

Commit

Permalink
[TypeScript] Keyword types can't be generic (#3863)
Browse files Browse the repository at this point in the history
Fix #3862.

Originally, type arguments were part of the regular type expression
structure, so the syntax would try to highlight type arguments after
any type. But actually, only ordinary named types can get them. In the
linked issue, this was causing problems with `as`-style type
assertions — when followed by a `<`, the type expression was eating it
and trying to parse type arguments, even when the preceding type does
not accept them.

According to the spec — I'm kidding, we all know the drill by now,
actually after a bunch of futzing around in AST Explorer — this should
work correctly in all cases I can think of.

In order for this to work in all cases, it's necessary to ensure that
all specially keyworded types are accounted for, so this PR also adds
the `true` and `false` singleton types.
  • Loading branch information
Thom1729 authored Oct 31, 2023
1 parent 4e10175 commit 83b037b
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 3 deletions.
18 changes: 15 additions & 3 deletions JavaScript/TypeScript.sublime-syntax
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,9 @@ contexts:
push:
- match: '{{identifier_name}}'
scope: support.class.js
set: ts-type-expression-end-no-line-terminator
set:
- ts-type-expression-end-no-line-terminator
- ts-optional-generic-type-arguments

- match: extends{{identifier_break}}
scope: keyword.operator.type.extends.js
Expand Down Expand Up @@ -889,8 +891,6 @@ contexts:
- ts-type-expression-end-no-line-terminator
- ts-type-expression-begin

- include: ts-generic-type-arguments

- include: else-pop

ts-generic-type-arguments:
Expand Down Expand Up @@ -1037,6 +1037,12 @@ contexts:
- match: 'boolean{{identifier_break}}'
scope: 'support.type.primitive.boolean.js'
pop: 1
- match: 'true{{identifier_break}}'
scope: 'support.type.primitive.boolean.true.js'
pop: 1
- match: 'false{{identifier_break}}'
scope: 'support.type.primitive.boolean.false.js'
pop: 1
- match: 'number{{identifier_break}}'
scope: 'support.type.primitive.number.js'
pop: 1
Expand All @@ -1062,7 +1068,13 @@ contexts:
ts-type-basic:
- match: '{{identifier_name}}'
scope: support.class.js
set: ts-optional-generic-type-arguments

ts-optional-generic-type-arguments:
- match: $
pop: 1
- include: ts-generic-type-arguments
- include: else-pop

ts-type-template-string:
- match: '`'
Expand Down
9 changes: 9 additions & 0 deletions JavaScript/tests/syntax_test_tsx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,15 @@ let x : T.U<V>;
// ^^^ meta.generic
// ^ support.class

let x : T.U
// ^^^ meta.type
// ^ support.class
// ^ punctuation.accessor
// ^ support.class

<V />;
// <- meta.jsx - meta.type

// This is invalid TSX as the TypeScript type assertion is parsed as a JSX tag
let strLength: number = (<string>someValue).length; // </string> );
// ^^^^^^^^ meta.tag - meta.assertion
Expand Down
44 changes: 44 additions & 0 deletions JavaScript/tests/syntax_test_typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,10 @@ let x: unknown;

let x: boolean;
// ^^^^^^^ support.type.primitive.boolean
let x: true;
// ^^^^ support.type.primitive.boolean.true
let x: false;
// ^^^^^ support.type.primitive.boolean.false
let x: number;
// ^^^^^^ support.type.primitive.number
let x: string;
Expand Down Expand Up @@ -1323,6 +1327,38 @@ let x: import ( "foo" ) . Bar ;
// ^ punctuation.accessor
// ^^^ support.class

let x: T.U;
// ^^^^ meta.type
// ^ support.class
// ^ punctuation.accessor
// ^ support.class
// ^ punctuation.terminator.statement

let x: T.U[];
// ^^^^ meta.type
// ^ support.class
// ^ punctuation.accessor
// ^ support.class
// ^^ storage.modifier.array
// ^ punctuation.terminator.statement

let x: T.U<V>[];
// ^^^^ meta.type
// ^ support.class
// ^ punctuation.accessor
// ^ support.class
// ^^^ meta.generic
// ^^ storage.modifier.array
// ^ punctuation.terminator.statement

let x: T.U
// ^^^^ meta.type
// ^ support.class
// ^ punctuation.accessor
// ^ support.class
[];
//<- meta.sequence - meta.type

foo < bar > ();
// ^^^ variable.function
// ^^^^^^^ meta.generic
Expand Down Expand Up @@ -1509,3 +1545,11 @@ type T = Foo | ... 100 more ... | Bar;
// ^ keyword.operator.type.union
// ^^^ support.class
// ^ punctuation.terminator.statement.empty

x as number < 3;
// ^ variable.other.readwrite
// ^^ keyword.operator.type
// ^^^^^^ meta.type support.type.primitive.number
// ^ keyword.operator.comparison
// ^ meta.number.integer.decimal constant.numeric.value
// ^ punctuation.terminator.statement

0 comments on commit 83b037b

Please sign in to comment.