Skip to content

Commit

Permalink
fix #2330: implement extends after infer in ts
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jun 18, 2022
1 parent 465ae2a commit 7034fca
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 2 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@

_Note that JavaScript feature transformation is very complex and allowing full customization of the set of supported syntax features could cause bugs in esbuild due to new interactions between multiple features that were never possible before. Consider this to be an experimental feature._

* Implement `extends` constraints on `infer` type variables ([#2330](https://github.com/evanw/esbuild/issues/2330))

TypeScript 4.7 introduced the ability to write an `extends` constraint after an `infer` type variable, which looks like this:

```ts
type FirstIfString<T> =
T extends [infer S extends string, ...unknown[]]
? S
: never;
```

You can read the blog post for more details: https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#extends-constraints-on-infer-type-variables. Previously this was a syntax error in esbuild but with this release, esbuild can now parse this syntax correctly.

* Allow `define` to match optional chain expressions ([#2324](https://github.com/evanw/esbuild/issues/2324))

Previously esbuild's `define` feature only matched member expressions that did not use optional chaining. With this release, esbuild will now also match those that use optional chaining:
Expand Down
18 changes: 17 additions & 1 deletion internal/js_parser/ts_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ const (
tsTypeIdentifierAsserts
tsTypeIdentifierPrefix
tsTypeIdentifierPrimitive
tsTypeIdentifierInfer
)

// Use a map to improve lookup speed
Expand All @@ -185,7 +186,6 @@ var tsTypeIdentifierMap = map[string]tsTypeIdentifierKind{

"keyof": tsTypeIdentifierPrefix,
"readonly": tsTypeIdentifierPrefix,
"infer": tsTypeIdentifierPrefix,

"any": tsTypeIdentifierPrimitive,
"never": tsTypeIdentifierPrimitive,
Expand All @@ -197,6 +197,8 @@ var tsTypeIdentifierMap = map[string]tsTypeIdentifierKind{
"boolean": tsTypeIdentifierPrimitive,
"bigint": tsTypeIdentifierPrimitive,
"symbol": tsTypeIdentifierPrimitive,

"infer": tsTypeIdentifierInfer,
}

func (p *parser) skipTypeScriptTypeWithOpts(level js_ast.L, opts skipTypeOpts) {
Expand Down Expand Up @@ -310,6 +312,20 @@ loop:
}
break loop

case tsTypeIdentifierInfer:
p.lexer.Next()

// "type Foo = Bar extends [infer T] ? T : null"
// "type Foo = Bar extends [infer T extends string] ? T : null"
if p.lexer.Token != js_lexer.TColon || (!opts.isIndexSignature && !opts.allowTupleLabels) {
p.lexer.Expect(js_lexer.TIdentifier)
if p.lexer.Token == js_lexer.TExtends {
p.lexer.Next()
p.skipTypeScriptType(js_ast.LPrefix)
}
}
break loop

case tsTypeIdentifierUnique:
p.lexer.Next()

Expand Down
5 changes: 4 additions & 1 deletion internal/js_parser/ts_parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ func TestTSTypes(t *testing.T) {
expectPrintedTS(t, "let x: [infer: string]", "let x;\n")
expectParseErrorTS(t, "let x: A extends B ? keyof : string", "<stdin>: ERROR: Unexpected \":\"\n")
expectParseErrorTS(t, "let x: A extends B ? readonly : string", "<stdin>: ERROR: Unexpected \":\"\n")
expectParseErrorTS(t, "let x: A extends B ? infer : string", "<stdin>: ERROR: Unexpected \":\"\n")
expectParseErrorTS(t, "let x: A extends B ? infer : string", "<stdin>: ERROR: Expected identifier but found \":\"\n")
expectParseErrorTS(t, "let x: {[new: string]: number}", "<stdin>: ERROR: Expected \"(\" but found \":\"\n")
expectParseErrorTS(t, "let x: {[import: string]: number}", "<stdin>: ERROR: Expected \"(\" but found \":\"\n")
expectParseErrorTS(t, "let x: {[typeof: string]: number}", "<stdin>: ERROR: Expected identifier but found \":\"\n")
Expand Down Expand Up @@ -177,6 +177,9 @@ func TestTSTypes(t *testing.T) {
expectPrintedTS(t, "type Foo = a.b \n & c.d", "")
expectPrintedTS(t, "type Foo = \n | a.b \n | c.d", "")
expectPrintedTS(t, "type Foo = \n & a.b \n & c.d", "")
expectPrintedTS(t, "type Foo = Bar extends [infer T] ? T : null", "")
expectPrintedTS(t, "type Foo = Bar extends [infer T extends string] ? T : null", "")
expectPrintedTS(t, "let x: A extends B<infer C extends D> ? D : never", "let x;\n")

expectPrintedTS(t, "let x: A.B<X.Y>", "let x;\n")
expectPrintedTS(t, "let x: A.B<X.Y>=2", "let x = 2;\n")
Expand Down

0 comments on commit 7034fca

Please sign in to comment.