Skip to content

Commit 600f1c2

Browse files
committed
Merge branch 'main' into goToDefContext
2 parents c96fbc7 + 6b43f82 commit 600f1c2

27 files changed

+1015
-115
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27031,42 +27031,32 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2703127031
}
2703227032
return type;
2703327033
}
27034-
27035-
// Check that right operand is a function type with a prototype property
2703627034
const rightType = getTypeOfExpression(expr.right);
2703727035
if (!isTypeDerivedFrom(rightType, globalFunctionType)) {
2703827036
return type;
2703927037
}
27040-
27041-
let targetType: Type | undefined;
27042-
const prototypeProperty = getPropertyOfType(rightType, "prototype" as __String);
27043-
if (prototypeProperty) {
27044-
// Target type is type of the prototype property
27045-
const prototypePropertyType = getTypeOfSymbol(prototypeProperty);
27046-
if (!isTypeAny(prototypePropertyType)) {
27047-
targetType = prototypePropertyType;
27048-
}
27049-
}
27050-
27051-
// Don't narrow from 'any' if the target type is exactly 'Object' or 'Function'
27052-
if (isTypeAny(type) && (targetType === globalObjectType || targetType === globalFunctionType)) {
27038+
const instanceType = mapType(rightType, getInstanceType);
27039+
// Don't narrow from `any` if the target type is exactly `Object` or `Function`, and narrow
27040+
// in the false branch only if the target is a non-empty object type.
27041+
if (isTypeAny(type) && (instanceType === globalObjectType || instanceType === globalFunctionType) ||
27042+
!assumeTrue && !(instanceType.flags & TypeFlags.Object && !isEmptyAnonymousObjectType(instanceType))) {
2705327043
return type;
2705427044
}
27045+
return getNarrowedType(type, instanceType, assumeTrue, /*checkDerived*/ true);
27046+
}
2705527047

27056-
if (!targetType) {
27057-
const constructSignatures = getSignaturesOfType(rightType, SignatureKind.Construct);
27058-
targetType = constructSignatures.length ?
27059-
getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature)))) :
27060-
emptyObjectType;
27048+
function getInstanceType(constructorType: Type) {
27049+
const prototypePropertyType = getTypeOfPropertyOfType(constructorType, "prototype" as __String);
27050+
if (prototypePropertyType && !isTypeAny(prototypePropertyType)) {
27051+
return prototypePropertyType;
2706127052
}
27062-
27063-
// We can't narrow a union based off instanceof without negated types see #31576 for more info
27064-
if (!assumeTrue && rightType.flags & TypeFlags.Union) {
27065-
const nonConstructorTypeInUnion = find((rightType as UnionType).types, (t) => !isConstructorType(t));
27066-
if (!nonConstructorTypeInUnion) return type;
27053+
const constructSignatures = getSignaturesOfType(constructorType, SignatureKind.Construct);
27054+
if (constructSignatures.length) {
27055+
return getUnionType(map(constructSignatures, signature => getReturnTypeOfSignature(getErasedSignature(signature))));
2706727056
}
27068-
27069-
return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true);
27057+
// We use the empty object type to indicate we don't know the type of objects created by
27058+
// this constructor function.
27059+
return emptyObjectType;
2707027060
}
2707127061

2707227062
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {

src/compiler/diagnosticMessages.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5558,6 +5558,22 @@
55585558
"category": "Message",
55595559
"code": 6412
55605560
},
5561+
"Entering conditional exports.": {
5562+
"category": "Message",
5563+
"code": 6413
5564+
},
5565+
"Resolved under condition '{0}'.": {
5566+
"category": "Message",
5567+
"code": 6414
5568+
},
5569+
"Failed to resolve under condition '{0}'.": {
5570+
"category": "Message",
5571+
"code": 6415
5572+
},
5573+
"Exiting conditional exports.": {
5574+
"category": "Message",
5575+
"code": 6416
5576+
},
55615577

55625578
"The expected type comes from property '{0}' which is declared here on type '{1}'": {
55635579
"category": "Message",

src/compiler/moduleNameResolver.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2584,19 +2584,26 @@ function getLoadModuleFromTargetImportOrExport(extensions: Extensions, state: Mo
25842584
}
25852585
else if (typeof target === "object" && target !== null) { // eslint-disable-line no-null/no-null
25862586
if (!Array.isArray(target)) {
2587+
traceIfEnabled(state, Diagnostics.Entering_conditional_exports);
25872588
for (const condition of getOwnKeys(target as MapLike<unknown>)) {
25882589
if (condition === "default" || state.conditions.indexOf(condition) >= 0 || isApplicableVersionedTypesKey(state.conditions, condition)) {
25892590
traceIfEnabled(state, Diagnostics.Matched_0_condition_1, isImports ? "imports" : "exports", condition);
25902591
const subTarget = (target as MapLike<unknown>)[condition];
25912592
const result = loadModuleFromTargetImportOrExport(subTarget, subpath, pattern, key);
25922593
if (result) {
2594+
traceIfEnabled(state, Diagnostics.Resolved_under_condition_0, condition);
2595+
traceIfEnabled(state, Diagnostics.Exiting_conditional_exports);
25932596
return result;
25942597
}
2598+
else {
2599+
traceIfEnabled(state, Diagnostics.Failed_to_resolve_under_condition_0, condition);
2600+
}
25952601
}
25962602
else {
25972603
traceIfEnabled(state, Diagnostics.Saw_non_matching_condition_0, condition);
25982604
}
25992605
}
2606+
traceIfEnabled(state, Diagnostics.Exiting_conditional_exports);
26002607
return undefined;
26012608
}
26022609
else {

src/compiler/moduleSpecifiers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,9 @@ function computeModuleSpecifiers(
371371
let redirectPathsSpecifiers: string[] | undefined;
372372
let relativeSpecifiers: string[] | undefined;
373373
for (const modulePath of modulePaths) {
374-
const specifier = tryGetModuleNameAsNodeModule(modulePath, info, importingSourceFile, host, compilerOptions, userPreferences, /*packageNameOnly*/ undefined, options.overrideImportMode);
374+
const specifier = modulePath.isInNodeModules
375+
? tryGetModuleNameAsNodeModule(modulePath, info, importingSourceFile, host, compilerOptions, userPreferences, /*packageNameOnly*/ undefined, options.overrideImportMode)
376+
: undefined;
375377
nodeModulesSpecifiers = append(nodeModulesSpecifiers, specifier);
376378
if (specifier && modulePath.isRedirect) {
377379
// If we got a specifier for a redirect, it was a bare package specifier (e.g. "@foo/bar",

src/services/completions.ts

Lines changed: 1 addition & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import {
77
CancellationToken,
88
canUsePropertyAccess,
99
CaseBlock,
10-
CaseClause,
1110
cast,
1211
CharacterCodes,
1312
ClassElement,
@@ -43,13 +42,11 @@ import {
4342
createTextSpanFromRange,
4443
Debug,
4544
Declaration,
46-
DefaultClause,
4745
Diagnostics,
4846
diagnosticToString,
4947
displayPart,
5048
EmitHint,
5149
EmitTextWriter,
52-
endsWith,
5350
EntityName,
5451
EnumMember,
5552
escapeSnippetText,
@@ -140,7 +137,6 @@ import {
140137
isConstructorDeclaration,
141138
isContextualKeyword,
142139
isDeclarationName,
143-
isDefaultClause,
144140
isDeprecatedDeclaration,
145141
isEntityName,
146142
isEnumMember,
@@ -185,7 +181,6 @@ import {
185181
isKeyword,
186182
isKnownSymbol,
187183
isLabeledStatement,
188-
isLiteralExpression,
189184
isLiteralImportTypeNode,
190185
isMemberName,
191186
isMethodDeclaration,
@@ -276,6 +271,7 @@ import {
276271
ModuleReference,
277272
moduleResolutionSupportsPackageJsonExportsAndImports,
278273
NamedImportBindings,
274+
newCaseClauseTracker,
279275
Node,
280276
NodeArray,
281277
NodeBuilderFlags,
@@ -288,7 +284,6 @@ import {
288284
ObjectTypeDeclaration,
289285
or,
290286
ParenthesizedTypeNode,
291-
parseBigInt,
292287
positionBelongsToNode,
293288
positionIsASICandidate,
294289
positionsAreOnSameLine,
@@ -1105,75 +1100,6 @@ function getExhaustiveCaseSnippets(
11051100
return undefined;
11061101
}
11071102

1108-
interface CaseClauseTracker {
1109-
addValue(value: string | number): void;
1110-
hasValue(value: string | number | PseudoBigInt): boolean;
1111-
}
1112-
1113-
function newCaseClauseTracker(checker: TypeChecker, clauses: readonly (CaseClause | DefaultClause)[]): CaseClauseTracker {
1114-
const existingStrings = new Set<string>();
1115-
const existingNumbers = new Set<number>();
1116-
const existingBigInts = new Set<string>();
1117-
1118-
for (const clause of clauses) {
1119-
if (!isDefaultClause(clause)) {
1120-
if (isLiteralExpression(clause.expression)) {
1121-
const expression = clause.expression;
1122-
switch (expression.kind) {
1123-
case SyntaxKind.NoSubstitutionTemplateLiteral:
1124-
case SyntaxKind.StringLiteral:
1125-
existingStrings.add(expression.text);
1126-
break;
1127-
case SyntaxKind.NumericLiteral:
1128-
existingNumbers.add(parseInt(expression.text));
1129-
break;
1130-
case SyntaxKind.BigIntLiteral:
1131-
const parsedBigInt = parseBigInt(endsWith(expression.text, "n") ? expression.text.slice(0, -1) : expression.text);
1132-
if (parsedBigInt) {
1133-
existingBigInts.add(pseudoBigIntToString(parsedBigInt));
1134-
}
1135-
break;
1136-
}
1137-
}
1138-
else {
1139-
const symbol = checker.getSymbolAtLocation(clause.expression);
1140-
if (symbol && symbol.valueDeclaration && isEnumMember(symbol.valueDeclaration)) {
1141-
const enumValue = checker.getConstantValue(symbol.valueDeclaration);
1142-
if (enumValue !== undefined) {
1143-
addValue(enumValue);
1144-
}
1145-
}
1146-
}
1147-
}
1148-
}
1149-
1150-
return {
1151-
addValue,
1152-
hasValue,
1153-
};
1154-
1155-
function addValue(value: string | number) {
1156-
switch (typeof value) {
1157-
case "string":
1158-
existingStrings.add(value);
1159-
break;
1160-
case "number":
1161-
existingNumbers.add(value);
1162-
}
1163-
}
1164-
1165-
function hasValue(value: string | number | PseudoBigInt): boolean {
1166-
switch (typeof value) {
1167-
case "string":
1168-
return existingStrings.has(value);
1169-
case "number":
1170-
return existingNumbers.has(value);
1171-
case "object":
1172-
return existingBigInts.has(pseudoBigIntToString(value));
1173-
}
1174-
}
1175-
}
1176-
11771103
function typeNodeToExpression(typeNode: TypeNode, languageVersion: ScriptTarget, quotePreference: QuotePreference): Expression | undefined {
11781104
switch (typeNode.kind) {
11791105
case SyntaxKind.TypeReference:

src/services/stringCompletions.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
arrayFrom,
55
CallLikeExpression,
66
CancellationToken,
7+
CaseClause,
78
changeExtension,
89
CharacterCodes,
910
combinePaths,
@@ -94,6 +95,7 @@ import {
9495
moduleResolutionUsesNodeModules,
9596
ModuleSpecifierEnding,
9697
moduleSpecifiers,
98+
newCaseClauseTracker,
9799
Node,
98100
normalizePath,
99101
normalizeSlashes,
@@ -275,7 +277,7 @@ function stringLiteralCompletionDetails(name: string, location: Node, completion
275277
return match && createCompletionDetailsForSymbol(match, checker, sourceFile, location, cancellationToken);
276278
}
277279
case StringLiteralCompletionKind.Types:
278-
return find(completion.types, t => t.value === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.typeElement, [textPart(name)]) : undefined;
280+
return find(completion.types, t => t.value === name) ? createCompletionDetails(name, ScriptElementKindModifier.none, ScriptElementKind.string, [textPart(name)]) : undefined;
279281
default:
280282
return Debug.assertNever(completion);
281283
}
@@ -415,12 +417,15 @@ function getStringLiteralCompletionEntries(sourceFile: SourceFile, node: StringL
415417
// var y = require("/*completion position*/");
416418
// export * from "/*completion position*/";
417419
return { kind: StringLiteralCompletionKind.Paths, paths: getStringLiteralCompletionsFromModuleNames(sourceFile, node, compilerOptions, host, typeChecker, preferences) };
418-
420+
case SyntaxKind.CaseClause:
421+
const tracker = newCaseClauseTracker(typeChecker, (node.parent as CaseClause).parent.clauses);
422+
const literals = fromContextualType().types.filter(literal => !tracker.hasValue(literal.value));
423+
return { kind: StringLiteralCompletionKind.Types, types: literals, isNewIdentifier: false };
419424
default:
420425
return fromContextualType();
421426
}
422427

423-
function fromContextualType(): StringLiteralCompletion {
428+
function fromContextualType(): StringLiteralCompletionsFromTypes {
424429
// Get completion for string literal from string literal type
425430
// i.e. var x: "hi" | "hello" = "/*completion position*/"
426431
return { kind: StringLiteralCompletionKind.Types, types: getStringLiteralTypes(getContextualTypeFromParent(node, typeChecker, ContextFlags.Completions)), isNewIdentifier: false };

0 commit comments

Comments
 (0)