diff --git a/tsc/internal/parser/parser.go b/tsc/internal/parser/parser.go index c670d64ad21c2..98cb7edcb821d 100644 --- a/tsc/internal/parser/parser.go +++ b/tsc/internal/parser/parser.go @@ -3086,7 +3086,7 @@ func (p *Parser) parseTypeArgumentsOfTypeReference() *ast.NodeList { } func (p *Parser) parseTypeArguments() *ast.NodeList { - if p.token == ast.KindLessThanToken { + if p.reScanLessThanToken() == ast.KindLessThanToken { return p.parseBracketedList(PCTypeArguments, (*Parser).parseType, ast.KindLessThanToken, ast.KindGreaterThanToken) } return nil @@ -3189,7 +3189,19 @@ func (p *Parser) parseTypeQuery() *ast.Node { // Make sure we perform ASI to prevent parsing the next line's type arguments as part of an instantiation expression var typeArguments *ast.NodeList if !p.hasPrecedingLineBreak() { - typeArguments = p.parseTypeArguments() + if p.token == ast.KindLessThanLessThanToken { + // `<<` may start type arguments whose first type is generic (`typeof f<(x: T) => T>`), but it + // may also be a left shift following a type in an expression (`x as typeof y << 1`). Only treat it + // as type arguments if they parse without errors. + state := p.mark() + typeArguments = p.parseTypeArguments() + if len(p.diagnostics) != state.diagnosticsLen { + p.rewind(state) + typeArguments = nil + } + } else { + typeArguments = p.parseTypeArguments() + } } return p.finishNode(p.factory.NewTypeQueryNode(entityName, typeArguments), pos) } diff --git a/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.js b/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.js new file mode 100644 index 0000000000000..1fca9a442a0fe --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.js @@ -0,0 +1,65 @@ +//// [tests/cases/compiler/parseGenericArrowRatherThanLeftShift2.tsx] //// + +//// [heritage.ts] +declare const f: (x: T) => T; +declare function g(): typeof B; +declare class B {} +interface I {} +interface J {} + +class C1 implements I<(x: T) => T> {} +class C2 extends B<(x: T) => T> {} +const C3 = class extends g()<(x: T) => T> {}; +interface I2 extends J<(x: T) => T> {} +type A1 = typeof f<(x: T) => T>; + +const e1 = f<(x: T) => T>; +const e2 = f<(x: T) => T>(f); + +// `<<` is still a left shift in expressions. +declare let n: number; +const s1 = n << 2; +const s2 = n< n; + +// `<<` after a type query in an expression is still a left shift. +declare const a: any; +const s4 = a as typeof n << 1; +const s5 = a satisfies typeof n << 1; + +//// [jsx.tsx] +declare namespace JSX { + interface Element {} + interface IntrinsicElements { + div: { x?: number }; + } +} +declare function Comp

(props: { value: P }): JSX.Element; + +const j1 = (x: T) => T> value={f} />; +const j2 = (x: T) => T> value={f}>; +const j3 =

; +const j4 =
; + + +//// [heritage.js] +"use strict"; +class C1 { +} +class C2 extends B { +} +const C3 = class extends g() { +}; +const e1 = f; +const e2 = f(f); +const s1 = n << 2; +const s2 = n << n; +const s3 = (n << n) > n; +const s4 = a << 1; +const s5 = a << 1; +//// [jsx.jsx] +"use strict"; +const j1 = ; +const j2 = ; +const j3 =
; +const j4 =
; diff --git a/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.symbols b/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.symbols new file mode 100644 index 0000000000000..f6465f73a40ae --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.symbols @@ -0,0 +1,174 @@ +//// [tests/cases/compiler/parseGenericArrowRatherThanLeftShift2.tsx] //// + +=== heritage.ts === +declare const f: (x: T) => T; +>f : Symbol(f, Decl(heritage.ts, 0, 13)) +>T : Symbol(T, Decl(heritage.ts, 0, 18)) +>x : Symbol(x, Decl(heritage.ts, 0, 21)) +>T : Symbol(T, Decl(heritage.ts, 0, 18)) +>T : Symbol(T, Decl(heritage.ts, 0, 18)) + +declare function g(): typeof B; +>g : Symbol(g, Decl(heritage.ts, 0, 32)) +>B : Symbol(B, Decl(heritage.ts, 1, 31)) + +declare class B {} +>B : Symbol(B, Decl(heritage.ts, 1, 31)) +>F : Symbol(F, Decl(heritage.ts, 2, 16)) + +interface I {} +>I : Symbol(I, Decl(heritage.ts, 2, 21)) +>F : Symbol(F, Decl(heritage.ts, 3, 12)) + +interface J {} +>J : Symbol(J, Decl(heritage.ts, 3, 17)) +>F : Symbol(F, Decl(heritage.ts, 4, 12)) + +class C1 implements I<(x: T) => T> {} +>C1 : Symbol(C1, Decl(heritage.ts, 4, 17)) +>I : Symbol(I, Decl(heritage.ts, 2, 21)) +>T : Symbol(T, Decl(heritage.ts, 6, 23)) +>x : Symbol(x, Decl(heritage.ts, 6, 26)) +>T : Symbol(T, Decl(heritage.ts, 6, 23)) +>T : Symbol(T, Decl(heritage.ts, 6, 23)) + +class C2 extends B<(x: T) => T> {} +>C2 : Symbol(C2, Decl(heritage.ts, 6, 40)) +>B : Symbol(B, Decl(heritage.ts, 1, 31)) +>T : Symbol(T, Decl(heritage.ts, 7, 20)) +>x : Symbol(x, Decl(heritage.ts, 7, 23)) +>T : Symbol(T, Decl(heritage.ts, 7, 20)) +>T : Symbol(T, Decl(heritage.ts, 7, 20)) + +const C3 = class extends g()<(x: T) => T> {}; +>C3 : Symbol(C3, Decl(heritage.ts, 8, 5)) +>g : Symbol(g, Decl(heritage.ts, 0, 32)) +>T : Symbol(T, Decl(heritage.ts, 8, 30)) +>x : Symbol(x, Decl(heritage.ts, 8, 33)) +>T : Symbol(T, Decl(heritage.ts, 8, 30)) +>T : Symbol(T, Decl(heritage.ts, 8, 30)) + +interface I2 extends J<(x: T) => T> {} +>I2 : Symbol(I2, Decl(heritage.ts, 8, 48)) +>J : Symbol(J, Decl(heritage.ts, 3, 17)) +>T : Symbol(T, Decl(heritage.ts, 9, 24)) +>x : Symbol(x, Decl(heritage.ts, 9, 27)) +>T : Symbol(T, Decl(heritage.ts, 9, 24)) +>T : Symbol(T, Decl(heritage.ts, 9, 24)) + +type A1 = typeof f<(x: T) => T>; +>A1 : Symbol(A1, Decl(heritage.ts, 9, 41)) +>f : Symbol(f, Decl(heritage.ts, 0, 13)) +>T : Symbol(T, Decl(heritage.ts, 10, 20)) +>x : Symbol(x, Decl(heritage.ts, 10, 23)) +>T : Symbol(T, Decl(heritage.ts, 10, 20)) +>T : Symbol(T, Decl(heritage.ts, 10, 20)) + +const e1 = f<(x: T) => T>; +>e1 : Symbol(e1, Decl(heritage.ts, 12, 5)) +>f : Symbol(f, Decl(heritage.ts, 0, 13)) +>T : Symbol(T, Decl(heritage.ts, 12, 14)) +>x : Symbol(x, Decl(heritage.ts, 12, 17)) +>T : Symbol(T, Decl(heritage.ts, 12, 14)) +>T : Symbol(T, Decl(heritage.ts, 12, 14)) + +const e2 = f<(x: T) => T>(f); +>e2 : Symbol(e2, Decl(heritage.ts, 13, 5)) +>f : Symbol(f, Decl(heritage.ts, 0, 13)) +>T : Symbol(T, Decl(heritage.ts, 13, 14)) +>x : Symbol(x, Decl(heritage.ts, 13, 17)) +>T : Symbol(T, Decl(heritage.ts, 13, 14)) +>T : Symbol(T, Decl(heritage.ts, 13, 14)) +>f : Symbol(f, Decl(heritage.ts, 0, 13)) + +// `<<` is still a left shift in expressions. +declare let n: number; +>n : Symbol(n, Decl(heritage.ts, 16, 11)) + +const s1 = n << 2; +>s1 : Symbol(s1, Decl(heritage.ts, 17, 5)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) + +const s2 = n<s2 : Symbol(s2, Decl(heritage.ts, 18, 5)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) + +const s3 = (n< n; +>s3 : Symbol(s3, Decl(heritage.ts, 19, 5)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) + +// `<<` after a type query in an expression is still a left shift. +declare const a: any; +>a : Symbol(a, Decl(heritage.ts, 22, 13)) + +const s4 = a as typeof n << 1; +>s4 : Symbol(s4, Decl(heritage.ts, 23, 5)) +>a : Symbol(a, Decl(heritage.ts, 22, 13)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) + +const s5 = a satisfies typeof n << 1; +>s5 : Symbol(s5, Decl(heritage.ts, 24, 5)) +>a : Symbol(a, Decl(heritage.ts, 22, 13)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) + +=== jsx.tsx === +declare namespace JSX { +>JSX : Symbol(JSX, Decl(jsx.tsx, 0, 0)) + + interface Element {} +>Element : Symbol(Element, Decl(jsx.tsx, 0, 23)) + + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(jsx.tsx, 1, 24)) + + div: { x?: number }; +>div : Symbol(IntrinsicElements.div, Decl(jsx.tsx, 2, 33)) +>x : Symbol(x, Decl(jsx.tsx, 3, 14)) + } +} +declare function Comp

(props: { value: P }): JSX.Element; +>Comp : Symbol(Comp, Decl(jsx.tsx, 5, 1)) +>P : Symbol(P, Decl(jsx.tsx, 6, 22)) +>props : Symbol(props, Decl(jsx.tsx, 6, 25)) +>value : Symbol(value, Decl(jsx.tsx, 6, 33)) +>P : Symbol(P, Decl(jsx.tsx, 6, 22)) +>JSX : Symbol(JSX, Decl(jsx.tsx, 0, 0)) +>Element : Symbol(JSX.Element, Decl(jsx.tsx, 0, 23)) + +const j1 = (x: T) => T> value={f} />; +>j1 : Symbol(j1, Decl(jsx.tsx, 8, 5)) +>Comp : Symbol(Comp, Decl(jsx.tsx, 5, 1)) +>T : Symbol(T, Decl(jsx.tsx, 8, 18)) +>x : Symbol(x, Decl(jsx.tsx, 8, 21)) +>T : Symbol(T, Decl(jsx.tsx, 8, 18)) +>T : Symbol(T, Decl(jsx.tsx, 8, 18)) +>value : Symbol(value, Decl(jsx.tsx, 8, 32)) +>f : Symbol(f, Decl(heritage.ts, 0, 13)) + +const j2 = (x: T) => T> value={f}>; +>j2 : Symbol(j2, Decl(jsx.tsx, 9, 5)) +>Comp : Symbol(Comp, Decl(jsx.tsx, 5, 1)) +>T : Symbol(T, Decl(jsx.tsx, 9, 18)) +>x : Symbol(x, Decl(jsx.tsx, 9, 21)) +>T : Symbol(T, Decl(jsx.tsx, 9, 18)) +>T : Symbol(T, Decl(jsx.tsx, 9, 18)) +>value : Symbol(value, Decl(jsx.tsx, 9, 32)) +>f : Symbol(f, Decl(heritage.ts, 0, 13)) +>Comp : Symbol(Comp, Decl(jsx.tsx, 5, 1)) + +const j3 =

; +>j3 : Symbol(j3, Decl(jsx.tsx, 10, 5)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(jsx.tsx, 2, 33)) +>x : Symbol(x, Decl(jsx.tsx, 10, 15)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) + +const j4 =
; +>j4 : Symbol(j4, Decl(jsx.tsx, 11, 5)) +>div : Symbol(JSX.IntrinsicElements.div, Decl(jsx.tsx, 2, 33)) +>x : Symbol(x, Decl(jsx.tsx, 11, 15)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) +>n : Symbol(n, Decl(heritage.ts, 16, 11)) + diff --git a/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.types b/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.types new file mode 100644 index 0000000000000..8e3baba18a6cd --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/parseGenericArrowRatherThanLeftShift2.types @@ -0,0 +1,149 @@ +//// [tests/cases/compiler/parseGenericArrowRatherThanLeftShift2.tsx] //// + +=== heritage.ts === +declare const f: (x: T) => T; +>f : (x: T) => T +>x : T + +declare function g(): typeof B; +>g : () => typeof B +>B : typeof B + +declare class B {} +>B : B + +interface I {} +interface J {} + +class C1 implements I<(x: T) => T> {} +>C1 : C1 +>x : T + +class C2 extends B<(x: T) => T> {} +>C2 : C2 +>B : B<(x: T) => T> +>x : T + +const C3 = class extends g()<(x: T) => T> {}; +>C3 : typeof C3 +>class extends g()<(x: T) => T> {} : typeof C3 +>g() : B<(x: T) => T> +>g : () => typeof B +>x : T + +interface I2 extends J<(x: T) => T> {} +>x : T + +type A1 = typeof f<(x: T) => T>; +>A1 : typeof f<(x: T) => T> +>f : (x: T) => T +>x : T + +const e1 = f<(x: T) => T>; +>e1 : (x: (x: T) => T) => (x: T) => T +>f<(x: T) => T> : (x: (x: T) => T) => (x: T) => T +>f : (x: T) => T +>x : T + +const e2 = f<(x: T) => T>(f); +>e2 : (x: T) => T +>f<(x: T) => T>(f) : (x: T) => T +>f : (x: T) => T +>x : T +>f : (x: T) => T + +// `<<` is still a left shift in expressions. +declare let n: number; +>n : number + +const s1 = n << 2; +>s1 : number +>n << 2 : number +>n : number +>2 : 2 + +const s2 = n<s2 : number +>n<n : number +>n : number + +const s3 = (n< n; +>s3 : boolean +>(n< n : boolean +>(n<n<n : number +>n : number +>n : number + +// `<<` after a type query in an expression is still a left shift. +declare const a: any; +>a : any + +const s4 = a as typeof n << 1; +>s4 : number +>a as typeof n << 1 : number +>a as typeof n : number +>a : any +>n : number +>1 : 1 + +const s5 = a satisfies typeof n << 1; +>s5 : number +>a satisfies typeof n << 1 : number +>a satisfies typeof n : any +>a : any +>n : number +>1 : 1 + +=== jsx.tsx === +declare namespace JSX { + interface Element {} + interface IntrinsicElements { + div: { x?: number }; +>div : { x?: number; } +>x : number | undefined + } +} +declare function Comp

(props: { value: P }): JSX.Element; +>Comp :

(props: { value: P; }) => JSX.Element +>props : { value: P; } +>value : P +>JSX : any + +const j1 = (x: T) => T> value={f} />; +>j1 : JSX.Element +>(x: T) => T> value={f} /> : JSX.Element +>Comp :

(props: { value: P; }) => JSX.Element +>x : T +>value : (x: T) => T +>f : (x: T) => T + +const j2 = (x: T) => T> value={f}>; +>j2 : JSX.Element +>(x: T) => T> value={f}> : JSX.Element +>Comp :

(props: { value: P; }) => JSX.Element +>x : T +>value : (x: T) => T +>f : (x: T) => T +>Comp :

(props: { value: P; }) => JSX.Element + +const j3 =

; +>j3 : JSX.Element +>
: JSX.Element +>div : any +>x : number +>n << 1 : number +>n : number +>1 : 1 + +const j4 =
; +>j4 : JSX.Element +>
: JSX.Element +>div : any +>x : number +>n<n : number +>n : number + diff --git a/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.errors.txt b/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.errors.txt new file mode 100644 index 0000000000000..d5523f6651b78 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.errors.txt @@ -0,0 +1,25 @@ +/b.js(1,15): error TS8023: JSDoc '@augments A' does not match the 'extends A' clause. + + +==== /a.d.ts (0 errors) ==== + declare class A { x: T } + interface I { y: T } + +==== /b.js (1 errors) ==== + /** @augments A<(x: T) => T> */ + ~ +!!! error TS8023: JSDoc '@augments A' does not match the 'extends A' clause. + class B extends A { + m() { + return this.x; + } + } + + /** @implements {I<(x: T) => T>} */ + class C { + constructor() { + /** @type {(x: T) => T} */ + this.y = x => x; + } + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.symbols b/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.symbols new file mode 100644 index 0000000000000..b494c063384ea --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.symbols @@ -0,0 +1,46 @@ +//// [tests/cases/conformance/jsdoc/jsdocAugments_genericFunctionTypeArgument.ts] //// + +=== /a.d.ts === +declare class A { x: T } +>A : Symbol(A, Decl(a.d.ts, 0, 0)) +>T : Symbol(T, Decl(a.d.ts, 0, 16)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>T : Symbol(T, Decl(a.d.ts, 0, 16)) + +interface I { y: T } +>I : Symbol(I, Decl(a.d.ts, 0, 27)) +>T : Symbol(T, Decl(a.d.ts, 1, 12)) +>y : Symbol(I.y, Decl(a.d.ts, 1, 16)) +>T : Symbol(T, Decl(a.d.ts, 1, 12)) + +=== /b.js === +/** @augments A<(x: T) => T> */ +class B extends A { +>B : Symbol(B, Decl(b.js, 0, 0)) +>A : Symbol(A, Decl(a.d.ts, 0, 0)) + + m() { +>m : Symbol(B.m, Decl(b.js, 1, 19)) + + return this.x; +>this.x : Symbol(A.x, Decl(a.d.ts, 0, 20)) +>this : Symbol(B, Decl(b.js, 0, 0)) +>x : Symbol(A.x, Decl(a.d.ts, 0, 20)) + } +} + +/** @implements {I<(x: T) => T>} */ +class C { +>C : Symbol(C, Decl(b.js, 5, 1)) + + constructor() { + /** @type {(x: T) => T} */ + this.y = x => x; +>this.y : Symbol(C.y, Decl(b.js, 9, 19)) +>this : Symbol(C, Decl(b.js, 5, 1)) +>y : Symbol(C.y, Decl(b.js, 9, 19)) +>x : Symbol(x, Decl(b.js, 11, 16)) +>x : Symbol(x, Decl(b.js, 11, 16)) + } +} + diff --git a/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.types b/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.types new file mode 100644 index 0000000000000..1b4d5ee29eec2 --- /dev/null +++ b/tsc/testdata/baselines/reference/conformance/jsdocAugments_genericFunctionTypeArgument.types @@ -0,0 +1,43 @@ +//// [tests/cases/conformance/jsdoc/jsdocAugments_genericFunctionTypeArgument.ts] //// + +=== /a.d.ts === +declare class A { x: T } +>A : A +>x : T + +interface I { y: T } +>y : T + +=== /b.js === +/** @augments A<(x: T) => T> */ +class B extends A { +>B : B +>A : A<(x: T) => T> + + m() { +>m : () => (x: T) => T + + return this.x; +>this.x : (x: T) => T +>this : this +>x : (x: T) => T + } +} + +/** @implements {I<(x: T) => T>} */ +class C { +>C : C + + constructor() { + /** @type {(x: T) => T} */ + this.y = x => x; +>this.y = x => x : (x: T) => T +>this.y : (x: T) => T +>this : this +>y : (x: T) => T +>x => x : (x: T) => T +>x : T +>x : T + } +} + diff --git a/tsc/testdata/tests/cases/compiler/parseGenericArrowRatherThanLeftShift2.tsx b/tsc/testdata/tests/cases/compiler/parseGenericArrowRatherThanLeftShift2.tsx new file mode 100644 index 0000000000000..fc627b4046a6f --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/parseGenericArrowRatherThanLeftShift2.tsx @@ -0,0 +1,46 @@ +// @target: es2015 +// @jsx: preserve +// @strict: true + +// https://github.com/microsoft/TypeScript/issues/47410 + +// @filename: heritage.ts +declare const f: (x: T) => T; +declare function g(): typeof B; +declare class B {} +interface I {} +interface J {} + +class C1 implements I<(x: T) => T> {} +class C2 extends B<(x: T) => T> {} +const C3 = class extends g()<(x: T) => T> {}; +interface I2 extends J<(x: T) => T> {} +type A1 = typeof f<(x: T) => T>; + +const e1 = f<(x: T) => T>; +const e2 = f<(x: T) => T>(f); + +// `<<` is still a left shift in expressions. +declare let n: number; +const s1 = n << 2; +const s2 = n< n; + +// `<<` after a type query in an expression is still a left shift. +declare const a: any; +const s4 = a as typeof n << 1; +const s5 = a satisfies typeof n << 1; + +// @filename: jsx.tsx +declare namespace JSX { + interface Element {} + interface IntrinsicElements { + div: { x?: number }; + } +} +declare function Comp

(props: { value: P }): JSX.Element; + +const j1 = (x: T) => T> value={f} />; +const j2 = (x: T) => T> value={f}>; +const j3 =

; +const j4 =
; diff --git a/tsc/testdata/tests/cases/conformance/jsdoc/jsdocAugments_genericFunctionTypeArgument.ts b/tsc/testdata/tests/cases/conformance/jsdoc/jsdocAugments_genericFunctionTypeArgument.ts new file mode 100644 index 0000000000000..ffdcda0fbd425 --- /dev/null +++ b/tsc/testdata/tests/cases/conformance/jsdoc/jsdocAugments_genericFunctionTypeArgument.ts @@ -0,0 +1,26 @@ +// @target: es2015 +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/47410 + +// @Filename: /a.d.ts +declare class A { x: T } +interface I { y: T } + +// @Filename: /b.js +/** @augments A<(x: T) => T> */ +class B extends A { + m() { + return this.x; + } +} + +/** @implements {I<(x: T) => T>} */ +class C { + constructor() { + /** @type {(x: T) => T} */ + this.y = x => x; + } +}