diff --git a/internal/checker/relater.go b/internal/checker/relater.go index e649e25b10..efd9f52b3d 100644 --- a/internal/checker/relater.go +++ b/internal/checker/relater.go @@ -2729,7 +2729,19 @@ func (r *Relater) hasExcessProperties(source *Type, target *Type, reportErrors b } checkTypes = reducedTarget.Distributed() } - for _, prop := range r.c.getPropertiesOfType(source) { + properties := r.c.getPropertiesOfType(source) + if isComparingJsxAttributes { + // Strada's insertion-ordered spread puts synthetic JSX children before explicit attributes. + for i, prop := range properties { + if i != 0 && prop.ValueDeclaration != nil && ast.IsPropertySignatureDeclaration(prop.ValueDeclaration) && ast.IsJsxAttributes(prop.ValueDeclaration.Parent) { + properties = slices.Clone(properties) + copy(properties[1:i+1], properties[:i]) + properties[0] = prop + break + } + } + } + for _, prop := range properties { if shouldCheckAsExcessProperty(prop, source.symbol) && !isIgnoredJsxProperty(source, prop) { if !r.c.isKnownProperty(reducedTarget, prop.Name, isComparingJsxAttributes) { if reportErrors { diff --git a/testdata/baselines/reference/compiler/externalLibraryMultilineJsxTsIgnore.symbols b/testdata/baselines/reference/compiler/externalLibraryMultilineJsxTsIgnore.symbols new file mode 100644 index 0000000000..c8570b582f --- /dev/null +++ b/testdata/baselines/reference/compiler/externalLibraryMultilineJsxTsIgnore.symbols @@ -0,0 +1,96 @@ +//// [tests/cases/compiler/externalLibraryMultilineJsxTsIgnore.ts] //// + +=== /node_modules/pkg/exports.d.ts === +export { x, y } from "./src/ui"; +>x : Symbol(x, Decl(exports.d.ts, 0, 8)) +>y : Symbol(y, Decl(exports.d.ts, 0, 11)) + +=== /node_modules/pkg/src/ui.tsx === +declare global { +>global : Symbol(global, Decl(ui.tsx, 0, 0)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(ui.tsx, 0, 16)) + + interface Element {} +>Element : Symbol(Element, Decl(ui.tsx, 1, 19)) + + interface IntrinsicAttributes {} +>IntrinsicAttributes : Symbol(IntrinsicAttributes, Decl(ui.tsx, 2, 28)) + + interface ElementChildrenAttribute { +>ElementChildrenAttribute : Symbol(ElementChildrenAttribute, Decl(ui.tsx, 3, 40)) + + children: {}; +>children : Symbol(ElementChildrenAttribute.children, Decl(ui.tsx, 4, 44)) + } + interface IntrinsicElements { +>IntrinsicElements : Symbol(IntrinsicElements, Decl(ui.tsx, 6, 9)) + + div: {}; +>div : Symbol(IntrinsicElements.div, Decl(ui.tsx, 7, 37)) + } + } +} + +declare const Component: (props: {}) => JSX.Element; +>Component : Symbol(Component, Decl(ui.tsx, 13, 13)) +>props : Symbol(props, Decl(ui.tsx, 13, 26)) +>JSX : Symbol(JSX, Decl(ui.tsx, 0, 16)) +>Element : Symbol(JSX.Element, Decl(ui.tsx, 1, 19)) + +export const x = ( +>x : Symbol(x, Decl(ui.tsx, 15, 12)) + + // @ts-ignore + Component : Symbol(Component, Decl(ui.tsx, 13, 13)) + + invalid="" +>invalid : Symbol(invalid, Decl(ui.tsx, 17, 14)) + + > +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(ui.tsx, 7, 37)) + + +>Component : Symbol(Component, Decl(ui.tsx, 13, 13)) + +); + +export const y = ( +>y : Symbol(y, Decl(ui.tsx, 24, 12)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(ui.tsx, 7, 37)) + + {/* @ts-ignore */} + Component : Symbol(Component, Decl(ui.tsx, 13, 13)) + + invalid="" +>invalid : Symbol(invalid, Decl(ui.tsx, 27, 18)) + + > +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(ui.tsx, 7, 37)) + + +>Component : Symbol(Component, Decl(ui.tsx, 13, 13)) + +
+>div : Symbol(JSX.IntrinsicElements.div, Decl(ui.tsx, 7, 37)) + +); + +=== /index.ts === +import { x, y } from "pkg"; +>x : Symbol(x, Decl(index.ts, 0, 8)) +>y : Symbol(y, Decl(index.ts, 0, 11)) + +x; +>x : Symbol(x, Decl(index.ts, 0, 8)) + +y; +>y : Symbol(y, Decl(index.ts, 0, 11)) + diff --git a/testdata/baselines/reference/compiler/externalLibraryMultilineJsxTsIgnore.types b/testdata/baselines/reference/compiler/externalLibraryMultilineJsxTsIgnore.types new file mode 100644 index 0000000000..e3ace77f01 --- /dev/null +++ b/testdata/baselines/reference/compiler/externalLibraryMultilineJsxTsIgnore.types @@ -0,0 +1,92 @@ +//// [tests/cases/compiler/externalLibraryMultilineJsxTsIgnore.ts] //// + +=== /node_modules/pkg/exports.d.ts === +export { x, y } from "./src/ui"; +>x : JSX.Element +>y : JSX.Element + +=== /node_modules/pkg/src/ui.tsx === +declare global { +>global : any + + namespace JSX { + interface Element {} + interface IntrinsicAttributes {} + interface ElementChildrenAttribute { + children: {}; +>children : {} + } + interface IntrinsicElements { + div: {}; +>div : {} + } + } +} + +declare const Component: (props: {}) => JSX.Element; +>Component : (props: {}) => JSX.Element +>props : {} +>JSX : any + +export const x = ( +>x : JSX.Element +>( // @ts-ignore
) : JSX.Element + + // @ts-ignore +
: JSX.Element +>Component : (props: {}) => JSX.Element + + invalid="" +>invalid : string + + > +
+>
: JSX.Element +>div : any + + +>Component : (props: {}) => JSX.Element + +); + +export const y = ( +>y : JSX.Element +>(
{/* @ts-ignore */}
) : JSX.Element + +
+>
{/* @ts-ignore */}
: JSX.Element +>div : any + + {/* @ts-ignore */} +
: JSX.Element +>Component : (props: {}) => JSX.Element + + invalid="" +>invalid : string + + > +
+>
: JSX.Element +>div : any + + +>Component : (props: {}) => JSX.Element + +
+>div : any + +); + +=== /index.ts === +import { x, y } from "pkg"; +>x : JSX.Element +>y : JSX.Element + +x; +>x : JSX.Element + +y; +>y : JSX.Element + diff --git a/testdata/tests/cases/compiler/externalLibraryMultilineJsxTsIgnore.ts b/testdata/tests/cases/compiler/externalLibraryMultilineJsxTsIgnore.ts new file mode 100644 index 0000000000..ada88098f8 --- /dev/null +++ b/testdata/tests/cases/compiler/externalLibraryMultilineJsxTsIgnore.ts @@ -0,0 +1,58 @@ +// @strict: true +// @skipLibCheck: true +// @jsx: preserve +// @module: esnext +// @moduleResolution: bundler +// @noEmit: true + +// @filename: /node_modules/pkg/package.json +{ + "name": "pkg", + "version": "1.0.0", + "types": "./exports.d.ts" +} + +// @filename: /node_modules/pkg/exports.d.ts +export { x, y } from "./src/ui"; + +// @filename: /node_modules/pkg/src/ui.tsx +declare global { + namespace JSX { + interface Element {} + interface IntrinsicAttributes {} + interface ElementChildrenAttribute { + children: {}; + } + interface IntrinsicElements { + div: {}; + } + } +} + +declare const Component: (props: {}) => JSX.Element; + +export const x = ( + // @ts-ignore + +
+ +); + +export const y = ( +
+ {/* @ts-ignore */} + +
+ +
+); + +// @filename: /index.ts +import { x, y } from "pkg"; + +x; +y;