Skip to content

Commit 61aadc4

Browse files
Vincent Boivintypescript-bot
Vincent Boivin
andauthored
fix(40320): Better errors when using properties/methods from newer versions of ECMAScript (microsoft#40650)
* Update package-lock.json * Suggesting a library for a missing property/method * Added more types and added tests * Added more tests to cover all the latest features * Added bigintarrays and dataview methods * Fixed typo in template * Transform old error message to use 2nd template slot * Removed test that has been split up between es2015 and es2016+ * Use empty arrays and remove unnecessary function call * merge * Added early bail-out and updated baselines * Implemented early bail-out (misread) Co-authored-by: TypeScript Bot <typescriptbot@microsoft.com>
1 parent 420df7f commit 61aadc4

23 files changed

+2533
-98
lines changed

src/compiler/checker.ts

+58-3
Original file line numberDiff line numberDiff line change
@@ -2023,7 +2023,15 @@ namespace ts {
20232023
}
20242024
}
20252025
if (!suggestion) {
2026-
error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg!));
2026+
if (nameArg) {
2027+
const lib = getSuggestedLibForNonExistentName(nameArg);
2028+
if (lib) {
2029+
error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg), lib);
2030+
}
2031+
else {
2032+
error(errorLocation, nameNotFoundMessage, diagnosticName(nameArg));
2033+
}
2034+
}
20272035
}
20282036
suggestionCount++;
20292037
}
@@ -20631,7 +20639,17 @@ namespace ts {
2063120639
case "WeakSet":
2063220640
case "Iterator":
2063320641
case "AsyncIterator":
20634-
return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_es2015_or_later;
20642+
case "SharedArrayBuffer":
20643+
case "Atomics":
20644+
case "AsyncIterable":
20645+
case "AsyncIterableIterator":
20646+
case "AsyncGenerator":
20647+
case "AsyncGeneratorFunction":
20648+
case "BigInt":
20649+
case "Reflect":
20650+
case "BigInt64Array":
20651+
case "BigUint64Array":
20652+
return Diagnostics.Cannot_find_name_0_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_1_or_later;
2063520653
default:
2063620654
if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) {
2063720655
return Diagnostics.No_value_exists_in_scope_for_the_shorthand_property_0_Either_declare_one_or_provide_an_initializer;
@@ -25887,7 +25905,15 @@ namespace ts {
2588725905
relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName);
2588825906
}
2588925907
else {
25890-
errorInfo = chainDiagnosticMessages(elaborateNeverIntersection(errorInfo, containingType), Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
25908+
const missingProperty = declarationNameToString(propNode);
25909+
const container = typeToString(containingType);
25910+
const lib = getSuggestedLibForNonExistentProperty(missingProperty, containingType);
25911+
if (lib) {
25912+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Do_you_need_to_change_your_target_library_Try_changing_the_lib_compiler_option_to_2_or_later, missingProperty, container, lib);
25913+
}
25914+
else {
25915+
errorInfo = chainDiagnosticMessages(elaborateNeverIntersection(errorInfo, containingType), Diagnostics.Property_0_does_not_exist_on_type_1, missingProperty, container);
25916+
}
2589125917
}
2589225918
}
2589325919
}
@@ -25903,6 +25929,34 @@ namespace ts {
2590325929
return prop !== undefined && prop.valueDeclaration && hasSyntacticModifier(prop.valueDeclaration, ModifierFlags.Static);
2590425930
}
2590525931

25932+
function getSuggestedLibForNonExistentName(name: __String | Identifier) {
25933+
const missingName = diagnosticName(name);
25934+
const allFeatures = getScriptTargetFeatures();
25935+
const libTargets = getOwnKeys(allFeatures);
25936+
for (const libTarget of libTargets) {
25937+
const containingTypes = getOwnKeys(allFeatures[libTarget]);
25938+
if (containingTypes !== undefined && contains(containingTypes, missingName)) {
25939+
return libTarget;
25940+
}
25941+
}
25942+
}
25943+
25944+
function getSuggestedLibForNonExistentProperty(missingProperty: string, containingType: Type) {
25945+
const container = getApparentType(containingType).symbol;
25946+
if (!container) {
25947+
return undefined;
25948+
}
25949+
const allFeatures = getScriptTargetFeatures();
25950+
const libTargets = getOwnKeys(allFeatures);
25951+
for (const libTarget of libTargets) {
25952+
const featuresOfLib = allFeatures[libTarget];
25953+
const featuresOfContainingType = featuresOfLib[symbolName(container)];
25954+
if (featuresOfContainingType !== undefined && contains(featuresOfContainingType, missingProperty)) {
25955+
return libTarget;
25956+
}
25957+
}
25958+
}
25959+
2590625960
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined {
2590725961
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
2590825962
}
@@ -25992,6 +26046,7 @@ namespace ts {
2599226046
*/
2599326047
function getSpellingSuggestionForName(name: string, symbols: Symbol[], meaning: SymbolFlags): Symbol | undefined {
2599426048
return getSpellingSuggestion(name, symbols, getCandidateName);
26049+
2599526050
function getCandidateName(candidate: Symbol) {
2599626051
const candidateName = symbolName(candidate);
2599726052
if (startsWith(candidateName, "\"")) {

src/compiler/diagnosticMessages.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -2197,6 +2197,10 @@
21972197
"category": "Error",
21982198
"code": 2549
21992199
},
2200+
"Property '{0}' does not exist on type '{1}'. Do you need to change your target library? Try changing the `lib` compiler option to '{2}' or later.": {
2201+
"category": "Error",
2202+
"code": 2550
2203+
},
22002204
"Property '{0}' does not exist on type '{1}'. Did you mean '{2}'?": {
22012205
"category": "Error",
22022206
"code": 2551
@@ -2313,7 +2317,7 @@
23132317
"category": "Error",
23142318
"code": 2582
23152319
},
2316-
"Cannot find name '{0}'. Do you need to change your target library? Try changing the `lib` compiler option to es2015 or later.": {
2320+
"Cannot find name '{0}'. Do you need to change your target library? Try changing the `lib` compiler option to '{1}' or later.": {
23172321
"category": "Error",
23182322
"code": 2583
23192323
},

src/compiler/utilities.ts

+70-1
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,76 @@ namespace ts {
540540
return emitNode && emitNode.flags || 0;
541541
}
542542

543+
interface ScriptTargetFeatures {
544+
[key: string]: { [key: string]: string[] | undefined };
545+
};
546+
547+
export function getScriptTargetFeatures(): ScriptTargetFeatures {
548+
return {
549+
es2015: {
550+
Array: ["find", "findIndex", "fill", "copyWithin", "entries", "keys", "values"],
551+
RegExp: ["flags", "sticky", "unicode"],
552+
Reflect: ["apply", "construct", "defineProperty", "deleteProperty", "get"," getOwnPropertyDescriptor", "getPrototypeOf", "has", "isExtensible", "ownKeys", "preventExtensions", "set", "setPrototypeOf"],
553+
ArrayConstructor: ["from", "of"],
554+
ObjectConstructor: ["assign", "getOwnPropertySymbols", "keys", "is", "setPrototypeOf"],
555+
NumberConstructor: ["isFinite", "isInteger", "isNaN", "isSafeInteger", "parseFloat", "parseInt"],
556+
Math: ["clz32", "imul", "sign", "log10", "log2", "log1p", "expm1", "cosh", "sinh", "tanh", "acosh", "asinh", "atanh", "hypot", "trunc", "fround", "cbrt"],
557+
Map: ["entries", "keys", "values"],
558+
Set: ["entries", "keys", "values"],
559+
Promise: ["all", "race", "reject", "resolve"],
560+
Symbol: ["for", "keyFor"],
561+
WeakMap: ["entries", "keys", "values"],
562+
WeakSet: ["entries", "keys", "values"],
563+
Iterator: emptyArray,
564+
AsyncIterator: emptyArray,
565+
String: ["codePointAt", "includes", "endsWith", "normalize", "repeat", "startsWith", "anchor", "big", "blink", "bold", "fixed", "fontcolor", "fontsize", "italics", "link", "small", "strike", "sub", "sup"],
566+
StringConstructor: ["fromCodePoint", "raw"]
567+
},
568+
es2016: {
569+
Array: ["includes"]
570+
},
571+
es2017: {
572+
Atomics: emptyArray,
573+
SharedArrayBuffer: emptyArray,
574+
String: ["padStart", "padEnd"],
575+
ObjectConstructor: ["values", "entries", "getOwnPropertyDescriptors"],
576+
DateTimeFormat: ["formatToParts"]
577+
},
578+
es2018: {
579+
Promise: ["finally"],
580+
RegExpMatchArray: ["groups"],
581+
RegExpExecArray: ["groups"],
582+
RegExp: ["dotAll"],
583+
Intl: ["PluralRules"],
584+
AsyncIterable: emptyArray,
585+
AsyncIterableIterator: emptyArray,
586+
AsyncGenerator: emptyArray,
587+
AsyncGeneratorFunction: emptyArray,
588+
},
589+
es2019: {
590+
Array: ["flat", "flatMap"],
591+
ObjectConstructor: ["fromEntries"],
592+
String: ["trimStart", "trimEnd", "trimLeft", "trimRight"],
593+
Symbol: ["description"]
594+
},
595+
es2020: {
596+
BigInt: emptyArray,
597+
BigInt64Array: emptyArray,
598+
BigUint64Array: emptyArray,
599+
Promise: ["allSettled"],
600+
SymbolConstructor: ["matchAll"],
601+
String: ["matchAll"],
602+
DataView: ["setBigInt64", "setBigUint64", "getBigInt64", "getBigUint64"],
603+
RelativeTimeFormat: ["format", "formatToParts", "resolvedOptions"]
604+
},
605+
esnext: {
606+
Promise: ["any"],
607+
String: ["replaceAll"],
608+
NumberFormat: ["formatToParts"]
609+
}
610+
};
611+
}
612+
543613
export const enum GetLiteralTextFlags {
544614
None = 0,
545615
NeverAsciiEscape = 1 << 0,
@@ -5733,7 +5803,6 @@ namespace ts {
57335803
if (arguments.length > 2) {
57345804
text = formatStringFromArgs(text, arguments, 2);
57355805
}
5736-
57375806
return {
57385807
messageText: text,
57395808
category: message.category,

0 commit comments

Comments
 (0)