From 4fce49530314dcb48ac9cb642d7528add4c01d39 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Fri, 19 Jun 2020 11:28:06 +0800 Subject: [PATCH] chore: resolve review --- src/compiler/checker.ts | 29 +++++++++++++++++++-------- src/compiler/commandLineParser.ts | 2 +- src/compiler/core.ts | 9 +-------- src/compiler/program.ts | 2 +- src/compiler/types.ts | 3 ++- src/services/codefixes/fixSpelling.ts | 4 ++-- 6 files changed, 28 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 94d511f92b5..2da9fdf5165 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, @@ -24852,7 +24853,7 @@ namespace ts { relatedInfo = createDiagnosticForNode(propNode, Diagnostics.Did_you_forget_to_use_await); } else { - const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType, /*isJsxAttr*/ false); + const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType); if (suggestion !== undefined) { const suggestedName = symbolName(suggestion); errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestedName); @@ -24875,12 +24876,24 @@ namespace ts { return prop !== undefined && prop.valueDeclaration && hasSyntacticModifier(prop.valueDeclaration, ModifierFlags.Static); } - function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type, isJsxAttr: boolean): Symbol | undefined { - return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value, isJsxAttr); + function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { + 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 defaultSuggestion = () => getSpellingSuggestionForName(strName, properties, SymbolFlags.Value); + const getCandidate = (expectedName: string) => properties.find(x => symbolName(x) === expectedName); + switch (strName) { + case "for": return getCandidate("htmlFor") || defaultSuggestion(); + case "class": return getCandidate("className") || defaultSuggestion(); + } + return defaultSuggestion(); + } + function getSuggestionForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type, isJsxAttr: boolean): string | undefined { - const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType, isJsxAttr); + const suggestion = (isJsxAttr ? getSuggestedSymbolForNonexistentJSXAttribute : getSuggestedSymbolForNonexistentProperty)(name, containingType); return suggestion && symbolName(suggestion); } @@ -24892,7 +24905,7 @@ namespace ts { // Sometimes the symbol is found when location is a return type of a function: `typeof x` and `x` is declared in the body of the function // So the table *contains* `x` but `x` isn't actually in scope. // However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion. - return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning, /*isJsxAttr*/ false); + return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning); }); return result; } @@ -24903,7 +24916,7 @@ namespace ts { } function getSuggestedSymbolForNonexistentModule(name: Identifier, targetModule: Symbol): Symbol | undefined { - return targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember, /*isJsxAttr*/ false); + return targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember); } function getSuggestionForNonexistentExport(name: Identifier, targetModule: Symbol): string | undefined { @@ -24953,8 +24966,8 @@ namespace ts { * (0.4 allows 1 substitution/transposition for every 5 characters, * and 1 insertion/deletion at 3 characters) */ - function getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags, isJsxAttr: boolean): Symbol | undefined { - return getSpellingSuggestion(name, symbols, getCandidateName, isJsxAttr); + function getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags): Symbol | undefined { + return getSpellingSuggestion(name, symbols, getCandidateName); function getCandidateName(candidate: Symbol) { const candidateName = symbolName(candidate); if (startsWith(candidateName, "\"")) { diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 49a455c2db1..a43e6244fcb 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1194,7 +1194,7 @@ namespace ts { createDiagnostics: (message: DiagnosticMessage, arg0: string, arg1?: string) => Diagnostic, unknownOptionErrorText?: string ) { - const possibleOption = getSpellingSuggestion(unknownOption, diagnostics.optionDeclarations, getOptionName, /*isJsxAttr*/ false); + const possibleOption = getSpellingSuggestion(unknownOption, diagnostics.optionDeclarations, getOptionName); return possibleOption ? createDiagnostics(diagnostics.unknownDidYouMeanDiagnostic, unknownOptionErrorText || unknownOption, possibleOption.name) : createDiagnostics(diagnostics.unknownOptionDiagnostic, unknownOptionErrorText || unknownOption); diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 45c758b1dea..4be5998d6f3 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1817,7 +1817,7 @@ namespace ts { * (0.4 allows 1 substitution/transposition for every 5 characters, * and 1 insertion/deletion at 3 characters) */ - export function getSpellingSuggestion(name: string, candidates: T[], getName: (candidate: T) => string | undefined, isJsxAttr: boolean): T | undefined { + export function getSpellingSuggestion(name: string, candidates: T[], getName: (candidate: T) => string | undefined): T | undefined { const maximumLengthDifference = Math.min(2, Math.floor(name.length * 0.34)); let bestDistance = Math.floor(name.length * 0.4) + 1; // If the best result isn't better than this, don't bother. let bestCandidate: T | undefined; @@ -1825,13 +1825,6 @@ namespace ts { const nameLowerCase = name.toLowerCase(); for (const candidate of candidates) { const candidateName = getName(candidate); - if (isJsxAttr) { - const htmlFor = name === "for" && candidateName === "htmlFor"; - const className = name === "class" && candidateName === "className"; - if (htmlFor || className) { - return candidate; - } - } if (candidateName !== undefined && Math.abs(candidateName.length - nameLowerCase.length) <= maximumLengthDifference) { const candidateNameLowerCase = candidateName.toLowerCase(); if (candidateNameLowerCase === nameLowerCase) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 784347c1f07..63873b9ac5d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -2784,7 +2784,7 @@ namespace ts { } else { const unqualifiedLibName = removeSuffix(removePrefix(libName, "lib."), ".d.ts"); - const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity, /*isJsxAttr*/ false); + const suggestion = getSpellingSuggestion(unqualifiedLibName, libs, identity); const message = suggestion ? Diagnostics.Cannot_find_lib_definition_for_0_Did_you_mean_1 : Diagnostics.Cannot_find_lib_definition_for_0; fileProcessingDiagnostics.add(createFileDiagnostic( file, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index dff2fef9146..52e1402bca5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4009,7 +4009,8 @@ namespace ts { */ /* @internal */ tryGetMemberInModuleExportsAndProperties(memberName: string, moduleSymbol: Symbol): Symbol | undefined; getApparentType(type: Type): Type; - /* @internal */ getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type, isJsxAttr: boolean): Symbol | undefined; + /* @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, isJsxAttr: boolean): 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 e5a04cfa1aa..0c63dc5ebfc 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -51,7 +51,7 @@ namespace ts.codefix { if (parent.flags & NodeFlags.OptionalChain) { containingType = checker.getNonNullableType(containingType); } - suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, containingType, /*isJsxAttr*/ false); + suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, containingType); } else if (isImportSpecifier(parent) && parent.name === node) { Debug.assertNode(node, isIdentifier, "Expected an identifier for spelling (import)"); @@ -65,7 +65,7 @@ namespace ts.codefix { Debug.assertNode(node, isIdentifier, "Expected an identifier for JSX attribute"); const tag = findAncestor(node, isJsxOpeningLikeElement)!; const props = checker.getContextualTypeForArgumentAtIndex(tag, 0); - suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, props!, /** isJsxAttr */ true); + suggestedSymbol = checker.getSuggestedSymbolForNonexistentJSXAttribute(node, props!); } else { const meaning = getMeaningFromLocation(node);