diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index caba37715d0a5..1ddd0c474a531 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -606,6 +606,7 @@ namespace ts {
getAllPossiblePropertiesOfTypes,
getSuggestedSymbolForNonexistentProperty,
getSuggestionForNonexistentProperty,
+ getSuggestedSymbolForNonexistentJSXAttribute,
getSuggestedSymbolForNonexistentSymbol: (location, name, meaning) => getSuggestedSymbolForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
getSuggestedSymbolForNonexistentModule,
@@ -16313,18 +16314,25 @@ namespace ts {
if (isJsxAttributes(errorNode) || isJsxOpeningLikeElement(errorNode) || isJsxOpeningLikeElement(errorNode.parent)) {
// JsxAttributes has an object-literal flag and undergo same type-assignablity check as normal object-literal.
// However, using an object-literal error message will be very confusing to the users so we give different a message.
- // TODO: Spelling suggestions for excess jsx attributes (needs new diagnostic messages)
if (prop.valueDeclaration && isJsxAttribute(prop.valueDeclaration) && getSourceFileOfNode(errorNode) === getSourceFileOfNode(prop.valueDeclaration.name)) {
// Note that extraneous children (as in `extra`) don't pass this check,
// since `children` is a SyntaxKind.PropertySignature instead of a SyntaxKind.JsxAttribute.
errorNode = prop.valueDeclaration.name;
}
- reportError(Diagnostics.Property_0_does_not_exist_on_type_1, symbolToString(prop), typeToString(errorTarget));
+ const propName = symbolToString(prop);
+ const suggestionSymbol = getSuggestedSymbolForNonexistentJSXAttribute(propName, errorTarget);
+ const suggestion = suggestionSymbol ? symbolToString(suggestionSymbol) : undefined;
+ if (suggestion) {
+ reportError(Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, propName, typeToString(errorTarget), suggestion);
+ }
+ else {
+ reportError(Diagnostics.Property_0_does_not_exist_on_type_1, propName, typeToString(errorTarget));
+ }
}
else {
// use the property's value declaration if the property is assigned inside the literal itself
const objectLiteralDeclaration = source.symbol && firstOrUndefined(source.symbol.declarations);
- let suggestion;
+ let suggestion: string | undefined;
if (prop.valueDeclaration && findAncestor(prop.valueDeclaration, d => d === objectLiteralDeclaration) && getSourceFileOfNode(objectLiteralDeclaration) === getSourceFileOfNode(errorNode)) {
const propDeclaration = prop.valueDeclaration as ObjectLiteralElementLike;
Debug.assertNode(propDeclaration, isObjectLiteralElementLike);
@@ -24877,6 +24885,15 @@ namespace ts {
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
}
+ function getSuggestedSymbolForNonexistentJSXAttribute(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined {
+ const strName = isString(name) ? name : idText(name);
+ const properties = getPropertiesOfType(containingType);
+ const jsxSpecific = strName === "for" ? find(properties, x => symbolName(x) === "htmlFor")
+ : strName === "class" ? find(properties, x => symbolName(x) === "className")
+ : undefined;
+ return jsxSpecific ?? getSpellingSuggestionForName(strName, properties, SymbolFlags.Value);
+ }
+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): string | undefined {
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
return suggestion && symbolName(suggestion);
@@ -28223,7 +28240,7 @@ namespace ts {
error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference);
return booleanType;
}
- if (expr.kind === SyntaxKind.PropertyAccessExpression && isPrivateIdentifier(expr.name)) {
+ if (isPropertyAccessExpression(expr) && isPrivateIdentifier(expr.name)) {
error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_private_identifier);
}
const links = getNodeLinks(expr);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index c9facd108a4c5..465af258f86da 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -4010,6 +4010,7 @@ namespace ts {
/* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined;
getApparentType(type: Type): Type;
/* @internal */ getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined;
+ /* @internal */ getSuggestedSymbolForNonexistentJSXAttribute(name: Identifier | string, containingType: Type): Symbol | undefined;
/* @internal */ getSuggestionForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): string | undefined;
/* @internal */ getSuggestedSymbolForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined;
/* @internal */ getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts
index b90c16c390826..0c63dc5ebfc95 100644
--- a/src/services/codefixes/fixSpelling.ts
+++ b/src/services/codefixes/fixSpelling.ts
@@ -7,12 +7,16 @@ namespace ts.codefix {
Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0.code,
Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0.code,
Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2.code,
+ // for JSX class components
+ Diagnostics.No_overload_matches_this_call.code,
+ // for JSX FC
+ Diagnostics.Type_0_is_not_assignable_to_type_1.code,
];
registerCodeFix({
errorCodes,
getCodeActions(context) {
- const { sourceFile } = context;
- const info = getInfo(sourceFile, context.span.start, context);
+ const { sourceFile, errorCode } = context;
+ const info = getInfo(sourceFile, context.span.start, context, errorCode);
if (!info) return undefined;
const { node, suggestedSymbol } = info;
const { target } = context.host.getCompilationSettings();
@@ -21,18 +25,23 @@ namespace ts.codefix {
},
fixIds: [fixId],
getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => {
- const info = getInfo(diag.file, diag.start, context);
+ const info = getInfo(diag.file, diag.start, context, diag.code);
const { target } = context.host.getCompilationSettings();
if (info) doChange(changes, context.sourceFile, info.node, info.suggestedSymbol, target!);
}),
});
- function getInfo(sourceFile: SourceFile, pos: number, context: CodeFixContextBase): { node: Node, suggestedSymbol: Symbol } | undefined {
+ function getInfo(sourceFile: SourceFile, pos: number, context: CodeFixContextBase, errorCode: number): { node: Node, suggestedSymbol: Symbol } | undefined {
// This is the identifier of the misspelled word. eg:
// this.speling = 1;
// ^^^^^^^
const node = getTokenAtPosition(sourceFile, pos);
const parent = node.parent;
+ // Only fix spelling for No_overload_matches_this_call emitted on the React class component
+ if ((
+ errorCode === Diagnostics.No_overload_matches_this_call.code ||
+ errorCode === Diagnostics.Type_0_is_not_assignable_to_type_1.code) &&
+ !isJsxAttribute(parent)) return undefined;
const checker = context.program.getTypeChecker();
let suggestedSymbol: Symbol | undefined;
@@ -52,6 +61,12 @@ namespace ts.codefix {
suggestedSymbol = checker.getSuggestedSymbolForNonexistentModule(node, resolvedSourceFile.symbol);
}
}
+ else if (isJsxAttribute(parent) && parent.name === node) {
+ Debug.assertNode(node, isIdentifier, "Expected an identifier for JSX attribute");
+ const tag = findAncestor(node, isJsxOpeningLikeElement)!;
+ const props = checker.getContextualTypeForArgumentAtIndex(tag, 0);
+ suggestedSymbol = checker.getSuggestedSymbolForNonexistentJSXAttribute(node, props!);
+ }
else {
const meaning = getMeaningFromLocation(node);
const name = getTextOfNode(node);
diff --git a/tests/baselines/reference/spellingSuggestionJSXAttribute.errors.txt b/tests/baselines/reference/spellingSuggestionJSXAttribute.errors.txt
new file mode 100644
index 0000000000000..cde21fb6a3861
--- /dev/null
+++ b/tests/baselines/reference/spellingSuggestionJSXAttribute.errors.txt
@@ -0,0 +1,79 @@
+tests/cases/compiler/spellingSuggestionJSXAttribute.tsx(8,4): error TS2322: Type '{ class: string; }' is not assignable to type 'DetailedHTMLProps, HTMLAnchorElement>'.
+ Property 'class' does not exist on type 'DetailedHTMLProps, HTMLAnchorElement>'. Did you mean 'className'?
+tests/cases/compiler/spellingSuggestionJSXAttribute.tsx(9,4): error TS2322: Type '{ for: string; }' is not assignable to type 'DetailedHTMLProps, HTMLAnchorElement>'.
+ Property 'for' does not exist on type 'DetailedHTMLProps, HTMLAnchorElement>'.
+tests/cases/compiler/spellingSuggestionJSXAttribute.tsx(10,8): error TS2322: Type '{ for: string; }' is not assignable to type 'DetailedHTMLProps, HTMLLabelElement>'.
+ Property 'for' does not exist on type 'DetailedHTMLProps, HTMLLabelElement>'. Did you mean 'htmlFor'?
+tests/cases/compiler/spellingSuggestionJSXAttribute.tsx(11,8): error TS2322: Type '{ for: string; class: string; }' is not assignable to type 'DetailedHTMLProps, HTMLLabelElement>'.
+ Property 'for' does not exist on type 'DetailedHTMLProps, HTMLLabelElement>'. Did you mean 'htmlFor'?
+tests/cases/compiler/spellingSuggestionJSXAttribute.tsx(12,9): error TS2769: No overload matches this call.
+ Overload 1 of 2, '(props: Readonly<{ className?: string; htmlFor?: string; }>): MyComp', gave the following error.
+ Type '{ class: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'.
+ Property 'class' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'. Did you mean 'className'?
+ Overload 2 of 2, '(props: { className?: string; htmlFor?: string; }, context?: any): MyComp', gave the following error.
+ Type '{ class: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'.
+ Property 'class' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'. Did you mean 'className'?
+tests/cases/compiler/spellingSuggestionJSXAttribute.tsx(13,10): error TS2322: Type '{ class: string; }' is not assignable to type 'IntrinsicAttributes & { className?: string; htmlFor?: string; }'.
+ Property 'class' does not exist on type 'IntrinsicAttributes & { className?: string; htmlFor?: string; }'. Did you mean 'className'?
+tests/cases/compiler/spellingSuggestionJSXAttribute.tsx(14,9): error TS2769: No overload matches this call.
+ Overload 1 of 2, '(props: Readonly<{ className?: string; htmlFor?: string; }>): MyComp', gave the following error.
+ Type '{ for: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'.
+ Property 'for' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'. Did you mean 'htmlFor'?
+ Overload 2 of 2, '(props: { className?: string; htmlFor?: string; }, context?: any): MyComp', gave the following error.
+ Type '{ for: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'.
+ Property 'for' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'. Did you mean 'htmlFor'?
+tests/cases/compiler/spellingSuggestionJSXAttribute.tsx(15,10): error TS2322: Type '{ for: string; }' is not assignable to type 'IntrinsicAttributes & { className?: string; htmlFor?: string; }'.
+ Property 'for' does not exist on type 'IntrinsicAttributes & { className?: string; htmlFor?: string; }'. Did you mean 'htmlFor'?
+
+
+==== tests/cases/compiler/spellingSuggestionJSXAttribute.tsx (8 errors) ====
+ ///
+ import * as React from "react";
+
+ function MyComp2(props: { className?: string, htmlFor?: string }) {
+ return null!;
+ }
+ class MyComp extends React.Component<{ className?: string, htmlFor?: string }> { }
+ ;
+ ~~~~~
+!!! error TS2322: Type '{ class: string; }' is not assignable to type 'DetailedHTMLProps, HTMLAnchorElement>'.
+!!! error TS2322: Property 'class' does not exist on type 'DetailedHTMLProps, HTMLAnchorElement>'. Did you mean 'className'?
+ ; // should have no fix
+ ~~~
+!!! error TS2322: Type '{ for: string; }' is not assignable to type 'DetailedHTMLProps, HTMLAnchorElement>'.
+!!! error TS2322: Property 'for' does not exist on type 'DetailedHTMLProps, HTMLAnchorElement>'.
+ ;
+ ~~~
+!!! error TS2322: Type '{ for: string; }' is not assignable to type 'DetailedHTMLProps, HTMLLabelElement>'.
+!!! error TS2322: Property 'for' does not exist on type 'DetailedHTMLProps, HTMLLabelElement>'. Did you mean 'htmlFor'?
+ ;
+ ~~~
+!!! error TS2322: Type '{ for: string; class: string; }' is not assignable to type 'DetailedHTMLProps, HTMLLabelElement>'.
+!!! error TS2322: Property 'for' does not exist on type 'DetailedHTMLProps, HTMLLabelElement>'. Did you mean 'htmlFor'?
+ ;
+ ~~~~~
+!!! error TS2769: No overload matches this call.
+!!! error TS2769: Overload 1 of 2, '(props: Readonly<{ className?: string; htmlFor?: string; }>): MyComp', gave the following error.
+!!! error TS2769: Type '{ class: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'.
+!!! error TS2769: Property 'class' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'. Did you mean 'className'?
+!!! error TS2769: Overload 2 of 2, '(props: { className?: string; htmlFor?: string; }, context?: any): MyComp', gave the following error.
+!!! error TS2769: Type '{ class: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'.
+!!! error TS2769: Property 'class' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'. Did you mean 'className'?
+ ;
+ ~~~~~
+!!! error TS2322: Type '{ class: string; }' is not assignable to type 'IntrinsicAttributes & { className?: string; htmlFor?: string; }'.
+!!! error TS2322: Property 'class' does not exist on type 'IntrinsicAttributes & { className?: string; htmlFor?: string; }'. Did you mean 'className'?
+ ;
+ ~~~
+!!! error TS2769: No overload matches this call.
+!!! error TS2769: Overload 1 of 2, '(props: Readonly<{ className?: string; htmlFor?: string; }>): MyComp', gave the following error.
+!!! error TS2769: Type '{ for: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'.
+!!! error TS2769: Property 'for' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'. Did you mean 'htmlFor'?
+!!! error TS2769: Overload 2 of 2, '(props: { className?: string; htmlFor?: string; }, context?: any): MyComp', gave the following error.
+!!! error TS2769: Type '{ for: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'.
+!!! error TS2769: Property 'for' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & Readonly<{ children?: ReactNode; }> & Readonly<{ className?: string; htmlFor?: string; }>'. Did you mean 'htmlFor'?
+ ;
+ ~~~
+!!! error TS2322: Type '{ for: string; }' is not assignable to type 'IntrinsicAttributes & { className?: string; htmlFor?: string; }'.
+!!! error TS2322: Property 'for' does not exist on type 'IntrinsicAttributes & { className?: string; htmlFor?: string; }'. Did you mean 'htmlFor'?
+
\ No newline at end of file
diff --git a/tests/baselines/reference/spellingSuggestionJSXAttribute.js b/tests/baselines/reference/spellingSuggestionJSXAttribute.js
new file mode 100644
index 0000000000000..00a0835a97531
--- /dev/null
+++ b/tests/baselines/reference/spellingSuggestionJSXAttribute.js
@@ -0,0 +1,54 @@
+//// [spellingSuggestionJSXAttribute.tsx]
+///
+import * as React from "react";
+
+function MyComp2(props: { className?: string, htmlFor?: string }) {
+ return null!;
+}
+class MyComp extends React.Component<{ className?: string, htmlFor?: string }> { }
+;
+; // should have no fix
+;
+;
+;
+;
+;
+;
+
+
+//// [spellingSuggestionJSXAttribute.js]
+"use strict";
+var __extends = (this && this.__extends) || (function () {
+ var extendStatics = function (d, b) {
+ extendStatics = Object.setPrototypeOf ||
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
+ function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
+ return extendStatics(d, b);
+ };
+ return function (d, b) {
+ extendStatics(d, b);
+ function __() { this.constructor = d; }
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
+ };
+})();
+exports.__esModule = true;
+///
+var React = require("react");
+function MyComp2(props) {
+ return null;
+}
+var MyComp = /** @class */ (function (_super) {
+ __extends(MyComp, _super);
+ function MyComp() {
+ return _super !== null && _super.apply(this, arguments) || this;
+ }
+ return MyComp;
+}(React.Component));
+React.createElement("a", { "class": "" });
+React.createElement("a", { "for": "" }); // should have no fix
+React.createElement("label", { "for": "" });
+React.createElement("label", { "for": "", "class": "" });
+React.createElement(MyComp, { "class": "" });
+React.createElement(MyComp2, { "class": "" });
+React.createElement(MyComp, { "for": "" });
+React.createElement(MyComp2, { "for": "" });
diff --git a/tests/baselines/reference/spellingSuggestionJSXAttribute.symbols b/tests/baselines/reference/spellingSuggestionJSXAttribute.symbols
new file mode 100644
index 0000000000000..b6cc2d6eb50d3
--- /dev/null
+++ b/tests/baselines/reference/spellingSuggestionJSXAttribute.symbols
@@ -0,0 +1,54 @@
+=== tests/cases/compiler/spellingSuggestionJSXAttribute.tsx ===
+///
+import * as React from "react";
+>React : Symbol(React, Decl(spellingSuggestionJSXAttribute.tsx, 1, 6))
+
+function MyComp2(props: { className?: string, htmlFor?: string }) {
+>MyComp2 : Symbol(MyComp2, Decl(spellingSuggestionJSXAttribute.tsx, 1, 31))
+>props : Symbol(props, Decl(spellingSuggestionJSXAttribute.tsx, 3, 17))
+>className : Symbol(className, Decl(spellingSuggestionJSXAttribute.tsx, 3, 25))
+>htmlFor : Symbol(htmlFor, Decl(spellingSuggestionJSXAttribute.tsx, 3, 45))
+
+ return null!;
+}
+class MyComp extends React.Component<{ className?: string, htmlFor?: string }> { }
+>MyComp : Symbol(MyComp, Decl(spellingSuggestionJSXAttribute.tsx, 5, 1))
+>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
+>React : Symbol(React, Decl(spellingSuggestionJSXAttribute.tsx, 1, 6))
+>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
+>className : Symbol(className, Decl(spellingSuggestionJSXAttribute.tsx, 6, 38))
+>htmlFor : Symbol(htmlFor, Decl(spellingSuggestionJSXAttribute.tsx, 6, 58))
+
+;
+>a : Symbol(JSX.IntrinsicElements.a, Decl(react16.d.ts, 2390, 41))
+>class : Symbol(class, Decl(spellingSuggestionJSXAttribute.tsx, 7, 2))
+
+; // should have no fix
+>a : Symbol(JSX.IntrinsicElements.a, Decl(react16.d.ts, 2390, 41))
+>for : Symbol(for, Decl(spellingSuggestionJSXAttribute.tsx, 8, 2))
+
+;
+>label : Symbol(JSX.IntrinsicElements.label, Decl(react16.d.ts, 2448, 102))
+>for : Symbol(for, Decl(spellingSuggestionJSXAttribute.tsx, 9, 6))
+
+;
+>label : Symbol(JSX.IntrinsicElements.label, Decl(react16.d.ts, 2448, 102))
+>for : Symbol(for, Decl(spellingSuggestionJSXAttribute.tsx, 10, 6))
+>class : Symbol(class, Decl(spellingSuggestionJSXAttribute.tsx, 10, 13))
+
+;
+>MyComp : Symbol(MyComp, Decl(spellingSuggestionJSXAttribute.tsx, 5, 1))
+>class : Symbol(class, Decl(spellingSuggestionJSXAttribute.tsx, 11, 7))
+
+;
+>MyComp2 : Symbol(MyComp2, Decl(spellingSuggestionJSXAttribute.tsx, 1, 31))
+>class : Symbol(class, Decl(spellingSuggestionJSXAttribute.tsx, 12, 8))
+
+;
+>MyComp : Symbol(MyComp, Decl(spellingSuggestionJSXAttribute.tsx, 5, 1))
+>for : Symbol(for, Decl(spellingSuggestionJSXAttribute.tsx, 13, 7))
+
+;
+>MyComp2 : Symbol(MyComp2, Decl(spellingSuggestionJSXAttribute.tsx, 1, 31))
+>for : Symbol(for, Decl(spellingSuggestionJSXAttribute.tsx, 14, 8))
+
diff --git a/tests/baselines/reference/spellingSuggestionJSXAttribute.types b/tests/baselines/reference/spellingSuggestionJSXAttribute.types
new file mode 100644
index 0000000000000..619ceb73e36ee
--- /dev/null
+++ b/tests/baselines/reference/spellingSuggestionJSXAttribute.types
@@ -0,0 +1,64 @@
+=== tests/cases/compiler/spellingSuggestionJSXAttribute.tsx ===
+///
+import * as React from "react";
+>React : typeof React
+
+function MyComp2(props: { className?: string, htmlFor?: string }) {
+>MyComp2 : (props: { className?: string; htmlFor?: string;}) => any
+>props : { className?: string; htmlFor?: string; }
+>className : string
+>htmlFor : string
+
+ return null!;
+>null! : null
+>null : null
+}
+class MyComp extends React.Component<{ className?: string, htmlFor?: string }> { }
+>MyComp : MyComp
+>React.Component : React.Component<{ className?: string; htmlFor?: string; }, {}, any>
+>React : typeof React
+>Component : typeof React.Component
+>className : string
+>htmlFor : string
+
+;
+> : JSX.Element
+>a : any
+>class : string
+
+; // should have no fix
+> : JSX.Element
+>a : any
+>for : string
+
+;
+> : JSX.Element
+>label : any
+>for : string
+
+;
+> : JSX.Element
+>label : any
+>for : string
+>class : string
+
+;
+> : JSX.Element
+>MyComp : typeof MyComp
+>class : string
+
+;
+> : JSX.Element
+>MyComp2 : (props: { className?: string; htmlFor?: string; }) => any
+>class : string
+
+;
+> : JSX.Element
+>MyComp : typeof MyComp
+>for : string
+
+;
+> : JSX.Element
+>MyComp2 : (props: { className?: string; htmlFor?: string; }) => any
+>for : string
+
diff --git a/tests/baselines/reference/tsxSpreadAttributesResolution14.errors.txt b/tests/baselines/reference/tsxSpreadAttributesResolution14.errors.txt
index f000257b9d543..631ec2158e81e 100644
--- a/tests/baselines/reference/tsxSpreadAttributesResolution14.errors.txt
+++ b/tests/baselines/reference/tsxSpreadAttributesResolution14.errors.txt
@@ -1,5 +1,5 @@
tests/cases/conformance/jsx/file.tsx(11,38): error TS2322: Type '{ Property1: true; property1: string; property2: number; }' is not assignable to type 'IntrinsicAttributes & AnotherComponentProps'.
- Property 'Property1' does not exist on type 'IntrinsicAttributes & AnotherComponentProps'.
+ Property 'Property1' does not exist on type 'IntrinsicAttributes & AnotherComponentProps'. Did you mean 'property1'?
==== tests/cases/conformance/jsx/file.tsx (1 errors) ====
@@ -16,7 +16,7 @@ tests/cases/conformance/jsx/file.tsx(11,38): error TS2322: Type '{ Property1: tr
~~~~~~~~~
!!! error TS2322: Type '{ Property1: true; property1: string; property2: number; }' is not assignable to type 'IntrinsicAttributes & AnotherComponentProps'.
-!!! error TS2322: Property 'Property1' does not exist on type 'IntrinsicAttributes & AnotherComponentProps'.
+!!! error TS2322: Property 'Property1' does not exist on type 'IntrinsicAttributes & AnotherComponentProps'. Did you mean 'property1'?
);
}
diff --git a/tests/baselines/reference/tsxSpreadAttributesResolution2.errors.txt b/tests/baselines/reference/tsxSpreadAttributesResolution2.errors.txt
index 3732b0f2aaee8..ebaa798cbaaea 100644
--- a/tests/baselines/reference/tsxSpreadAttributesResolution2.errors.txt
+++ b/tests/baselines/reference/tsxSpreadAttributesResolution2.errors.txt
@@ -6,7 +6,7 @@ tests/cases/conformance/jsx/file.tsx(23,10): error TS2322: Type '{ x: number; y:
Types of property 'x' are incompatible.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/jsx/file.tsx(24,40): error TS2322: Type '{ X: string; x: number; y: "2"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & PoisonedProp & { children?: ReactNode; }'.
- Property 'X' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & PoisonedProp & { children?: ReactNode; }'.
+ Property 'X' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & PoisonedProp & { children?: ReactNode; }'. Did you mean 'x'?
==== tests/cases/conformance/jsx/file.tsx (6 errors) ====
@@ -50,4 +50,4 @@ tests/cases/conformance/jsx/file.tsx(24,40): error TS2322: Type '{ X: string; x:
let w1 = ;
~
!!! error TS2322: Type '{ X: string; x: number; y: "2"; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes & PoisonedProp & { children?: ReactNode; }'.
-!!! error TS2322: Property 'X' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & PoisonedProp & { children?: ReactNode; }'.
\ No newline at end of file
+!!! error TS2322: Property 'X' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes & PoisonedProp & { children?: ReactNode; }'. Did you mean 'x'?
\ No newline at end of file
diff --git a/tests/baselines/reference/tsxStatelessFunctionComponents1.errors.txt b/tests/baselines/reference/tsxStatelessFunctionComponents1.errors.txt
index 99812eeaad3ba..8c20009063a5d 100644
--- a/tests/baselines/reference/tsxStatelessFunctionComponents1.errors.txt
+++ b/tests/baselines/reference/tsxStatelessFunctionComponents1.errors.txt
@@ -1,5 +1,5 @@
tests/cases/conformance/jsx/file.tsx(19,16): error TS2322: Type '{ naaame: string; }' is not assignable to type 'IntrinsicAttributes & { name: string; }'.
- Property 'naaame' does not exist on type 'IntrinsicAttributes & { name: string; }'.
+ Property 'naaame' does not exist on type 'IntrinsicAttributes & { name: string; }'. Did you mean 'name'?
tests/cases/conformance/jsx/file.tsx(27,15): error TS2322: Type 'number' is not assignable to type 'string'.
tests/cases/conformance/jsx/file.tsx(29,15): error TS2322: Type '{ naaaaaaame: string; }' is not assignable to type 'IntrinsicAttributes & { name?: string; }'.
Property 'naaaaaaame' does not exist on type 'IntrinsicAttributes & { name?: string; }'.
@@ -34,7 +34,7 @@ tests/cases/conformance/jsx/file.tsx(45,11): error TS2559: Type '{ prop1: boolea
let b = ;
~~~~~~
!!! error TS2322: Type '{ naaame: string; }' is not assignable to type 'IntrinsicAttributes & { name: string; }'.
-!!! error TS2322: Property 'naaame' does not exist on type 'IntrinsicAttributes & { name: string; }'.
+!!! error TS2322: Property 'naaame' does not exist on type 'IntrinsicAttributes & { name: string; }'. Did you mean 'name'?
// OK
let c = ;
diff --git a/tests/cases/compiler/spellingSuggestionJSXAttribute.tsx b/tests/cases/compiler/spellingSuggestionJSXAttribute.tsx
new file mode 100644
index 0000000000000..e44de6cb7fa29
--- /dev/null
+++ b/tests/cases/compiler/spellingSuggestionJSXAttribute.tsx
@@ -0,0 +1,16 @@
+// @jsx: react
+///
+import * as React from "react";
+
+function MyComp2(props: { className?: string, htmlFor?: string }) {
+ return null!;
+}
+class MyComp extends React.Component<{ className?: string, htmlFor?: string }> { }
+;
+; // should have no fix
+;
+;
+;
+;
+;
+;