Skip to content

Commit fe1ee79

Browse files
committed
Merge branch 'main' of https://github.com/microsoft/TypeScript into feat/51086
2 parents 398e119 + a05b7ec commit fe1ee79

File tree

57 files changed

+2450
-48
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2450
-48
lines changed

package-lock.json

Lines changed: 18 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/compiler/checker.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11353,7 +11353,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1135311353

1135411354
// The outer type parameters are those defined by enclosing generic classes, methods, or functions.
1135511355
function getOuterTypeParametersOfClassOrInterface(symbol: Symbol): TypeParameter[] | undefined {
11356-
const declaration = symbol.flags & SymbolFlags.Class ? symbol.valueDeclaration : getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration)!;
11356+
const declaration = (symbol.flags & SymbolFlags.Class || symbol.flags & SymbolFlags.Function)
11357+
? symbol.valueDeclaration
11358+
: symbol.declarations?.find(decl => {
11359+
if (decl.kind === SyntaxKind.InterfaceDeclaration) {
11360+
return true;
11361+
}
11362+
if (decl.kind !== SyntaxKind.VariableDeclaration) {
11363+
return false;
11364+
}
11365+
const initializer = (decl as VariableDeclaration).initializer;
11366+
return !!initializer && (initializer.kind === SyntaxKind.FunctionExpression || initializer.kind === SyntaxKind.ArrowFunction);
11367+
})!;
1135711368
Debug.assert(!!declaration, "Class was missing valueDeclaration -OR- non-class had no interface declarations");
1135811369
return getOuterTypeParameters(declaration);
1135911370
}
@@ -24592,8 +24603,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2459224603
// parameter and has inferences that would conflict. Otherwise, we use the contra-variant inference.
2459324604
const useCovariantType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
2459424605
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) &&
24595-
every(context.inferences, other => other === inference ||
24596-
getConstraintOfTypeParameter(other.typeParameter) !== inference.typeParameter ||
24606+
every(context.inferences, other =>
24607+
other !== inference && getConstraintOfTypeParameter(other.typeParameter) !== inference.typeParameter ||
2459724608
every(other.candidates, t => isTypeSubtypeOf(t, inferredCovariantType)));
2459824609
inferredType = useCovariantType ? inferredCovariantType : getContravariantInference(inference);
2459924610
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3864,6 +3864,10 @@
38643864
"category": "Error",
38653865
"code": 4084
38663866
},
3867+
"Extends clause for inferred type '{0}' has or is using private name '{1}'.": {
3868+
"category": "Error",
3869+
"code": 4085
3870+
},
38673871
"Conflicting definitions for '{0}' found at '{1}' and '{2}'. Consider installing a specific version of this library to resolve the conflict.": {
38683872
"category": "Error",
38693873
"code": 4090

src/compiler/moduleNameResolver.ts

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ export interface ModuleResolutionState {
259259
requestContainingDirectory: string | undefined;
260260
reportDiagnostic: DiagnosticReporter;
261261
isConfigLookup: boolean;
262+
candidateIsFromPackageJsonField: boolean;
262263
}
263264

264265
/** Just the fields that we use for module resolution.
@@ -526,6 +527,7 @@ export function resolveTypeReferenceDirective(typeReferenceDirectiveName: string
526527
requestContainingDirectory: containingDirectory,
527528
reportDiagnostic: diag => void diagnostics.push(diag),
528529
isConfigLookup: false,
530+
candidateIsFromPackageJsonField: false,
529531
};
530532
let resolved = primaryLookup();
531533
let primary = true;
@@ -1652,6 +1654,7 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa
16521654
requestContainingDirectory: containingDirectory,
16531655
reportDiagnostic: diag => void diagnostics.push(diag),
16541656
isConfigLookup,
1657+
candidateIsFromPackageJsonField: false,
16551658
};
16561659

16571660
if (traceEnabled && getEmitModuleResolutionKind(compilerOptions) >= ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(compilerOptions) <= ModuleResolutionKind.NodeNext) {
@@ -1948,7 +1951,7 @@ function tryAddingExtensions(candidate: string, extensions: Extensions, original
19481951

19491952
function tryExtension(ext: string, resolvedUsingTsExtension?: boolean): PathAndExtension | undefined {
19501953
const path = tryFile(candidate + ext, onlyRecordFailures, state);
1951-
return path === undefined ? undefined : { path, ext, resolvedUsingTsExtension };
1954+
return path === undefined ? undefined : { path, ext, resolvedUsingTsExtension: !state.candidateIsFromPackageJsonField && resolvedUsingTsExtension };
19521955
}
19531956
}
19541957

@@ -2111,6 +2114,7 @@ export function getTemporaryModuleResolutionState(packageJsonInfoCache: PackageJ
21112114
requestContainingDirectory: undefined,
21122115
reportDiagnostic: noop,
21132116
isConfigLookup: false,
2117+
candidateIsFromPackageJsonField: false,
21142118
};
21152119
}
21162120

@@ -2232,11 +2236,14 @@ function loadNodeModuleFromDirectoryWorker(extensions: Extensions, candidate: st
22322236
// (technically it only emits a deprecation warning in esm packages right now, but that's probably
22332237
// enough to mean we don't need to support it)
22342238
const features = state.features;
2239+
const candidateIsFromPackageJsonField = state.candidateIsFromPackageJsonField;
2240+
state.candidateIsFromPackageJsonField = true;
22352241
if (jsonContent?.type !== "module") {
22362242
state.features &= ~NodeResolutionFeatures.EsmMode;
22372243
}
22382244
const result = nodeLoadModuleByRelativeName(expandedExtensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ false);
22392245
state.features = features;
2246+
state.candidateIsFromPackageJsonField = candidateIsFromPackageJsonField;
22402247
return result;
22412248
};
22422249

@@ -2313,7 +2320,18 @@ function loadModuleFromSelfNameReference(extensions: Extensions, moduleName: str
23132320
return undefined;
23142321
}
23152322
const trailingParts = parts.slice(nameParts.length);
2316-
return loadModuleFromExports(scope, extensions, !length(trailingParts) ? "." : `.${directorySeparator}${trailingParts.join(directorySeparator)}`, state, cache, redirectedReference);
2323+
const subpath = !length(trailingParts) ? "." : `.${directorySeparator}${trailingParts.join(directorySeparator)}`;
2324+
// Maybe TODO: splitting extensions into two priorities should be unnecessary, except
2325+
// https://github.com/microsoft/TypeScript/issues/50762 makes the behavior different.
2326+
// As long as that bug exists, we need to do two passes here in self-name loading
2327+
// in order to be consistent with (non-self) library-name loading in
2328+
// `loadModuleFromNearestNodeModulesDirectoryWorker`, which uses two passes in order
2329+
// to prioritize `@types` packages higher up the directory tree over untyped
2330+
// implementation packages.
2331+
const priorityExtensions = extensions & (Extensions.TypeScript | Extensions.Declaration);
2332+
const secondaryExtensions = extensions & ~(Extensions.TypeScript | Extensions.Declaration);
2333+
return loadModuleFromExports(scope, priorityExtensions, subpath, state, cache, redirectedReference)
2334+
|| loadModuleFromExports(scope, secondaryExtensions, subpath, state, cache, redirectedReference);
23172335
}
23182336

23192337
function loadModuleFromExports(scope: PackageJsonInfo, extensions: Extensions, subpath: string, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): SearchResult<Resolved> {
@@ -2753,27 +2771,31 @@ function loadModuleFromImmediateNodeModulesDirectory(extensions: Extensions, mod
27532771

27542772
function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, moduleName: string, nodeModulesDirectory: string, nodeModulesDirectoryExists: boolean, state: ModuleResolutionState, cache: ModuleResolutionCache | undefined, redirectedReference: ResolvedProjectReference | undefined): Resolved | undefined {
27552773
const candidate = normalizePath(combinePaths(nodeModulesDirectory, moduleName));
2774+
const { packageName, rest } = parsePackageName(moduleName);
2775+
const packageDirectory = combinePaths(nodeModulesDirectory, packageName);
27562776

2777+
let rootPackageInfo: PackageJsonInfo | undefined;
27572778
// First look for a nested package.json, as in `node_modules/foo/bar/package.json`.
27582779
let packageInfo = getPackageJsonInfo(candidate, !nodeModulesDirectoryExists, state);
27592780
// But only if we're not respecting export maps (if we are, we might redirect around this location)
2760-
if (!(state.features & NodeResolutionFeatures.Exports)) {
2761-
if (packageInfo) {
2762-
const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
2763-
if (fromFile) {
2764-
return noPackageId(fromFile);
2765-
}
2766-
2767-
const fromDirectory = loadNodeModuleFromDirectoryWorker(
2768-
extensions,
2769-
candidate,
2770-
!nodeModulesDirectoryExists,
2771-
state,
2772-
packageInfo.contents.packageJsonContent,
2773-
getVersionPathsOfPackageJsonInfo(packageInfo, state),
2774-
);
2775-
return withPackageId(packageInfo, fromDirectory);
2781+
if (rest !== "" && packageInfo && (
2782+
!(state.features & NodeResolutionFeatures.Exports) ||
2783+
!hasProperty((rootPackageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state))?.contents.packageJsonContent ?? emptyArray, "exports")
2784+
)) {
2785+
const fromFile = loadModuleFromFile(extensions, candidate, !nodeModulesDirectoryExists, state);
2786+
if (fromFile) {
2787+
return noPackageId(fromFile);
27762788
}
2789+
2790+
const fromDirectory = loadNodeModuleFromDirectoryWorker(
2791+
extensions,
2792+
candidate,
2793+
!nodeModulesDirectoryExists,
2794+
state,
2795+
packageInfo.contents.packageJsonContent,
2796+
getVersionPathsOfPackageJsonInfo(packageInfo, state),
2797+
);
2798+
return withPackageId(packageInfo, fromDirectory);
27772799
}
27782800

27792801
const loader: ResolutionKindSpecificLoader = (extensions, candidate, onlyRecordFailures, state) => {
@@ -2800,11 +2822,9 @@ function loadModuleFromSpecificNodeModulesDirectory(extensions: Extensions, modu
28002822
return withPackageId(packageInfo, pathAndExtension);
28012823
};
28022824

2803-
const { packageName, rest } = parsePackageName(moduleName);
2804-
const packageDirectory = combinePaths(nodeModulesDirectory, packageName);
28052825
if (rest !== "") {
28062826
// Previous `packageInfo` may have been from a nested package.json; ensure we have the one from the package root now.
2807-
packageInfo = getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state);
2827+
packageInfo = rootPackageInfo ?? getPackageJsonInfo(packageDirectory, !nodeModulesDirectoryExists, state);
28082828
}
28092829
// package exports are higher priority than file/directory/typesVersions lookups and (and, if there's exports present, blocks them)
28102830
if (packageInfo && packageInfo.contents.packageJsonContent.exports && state.features & NodeResolutionFeatures.Exports) {
@@ -2935,6 +2955,7 @@ export function classicNameResolver(moduleName: string, containingFile: string,
29352955
requestContainingDirectory: containingDirectory,
29362956
reportDiagnostic: diag => void diagnostics.push(diag),
29372957
isConfigLookup: false,
2958+
candidateIsFromPackageJsonField: false,
29382959
};
29392960

29402961
const resolved =
@@ -3015,6 +3036,7 @@ export function loadModuleFromGlobalCache(moduleName: string, projectName: strin
30153036
requestContainingDirectory: undefined,
30163037
reportDiagnostic: diag => void diagnostics.push(diag),
30173038
isConfigLookup: false,
3039+
candidateIsFromPackageJsonField: false,
30183040
};
30193041
const resolved = loadModuleFromImmediateNodeModulesDirectory(Extensions.Declaration, moduleName, globalCache, state, /*typesScopeOnly*/ false, /*cache*/ undefined, /*redirectedReference*/ undefined);
30203042
return createResolvedModuleWithFailedLookupLocations(

src/compiler/transformers/declarations/diagnostics.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ export function createGetSymbolAccessibilityDiagnosticForNode(node: DeclarationD
500500
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_function_has_or_is_using_private_name_1;
501501
break;
502502

503+
case SyntaxKind.InferType:
504+
diagnosticMessage = Diagnostics.Extends_clause_for_inferred_type_0_has_or_is_using_private_name_1;
505+
break;
506+
503507
case SyntaxKind.TypeAliasDeclaration:
504508
diagnosticMessage = Diagnostics.Type_parameter_0_of_exported_type_alias_has_or_is_using_private_name_1;
505509
break;

tests/baselines/reference/circularBaseTypes.errors.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
tests/cases/compiler/circularBaseTypes.ts(4,11): error TS2310: Type 'M2' recursively references itself as a base type.
22
tests/cases/compiler/circularBaseTypes.ts(5,6): error TS2456: Type alias 'M3' circularly references itself.
3+
tests/cases/compiler/circularBaseTypes.ts(13,21): error TS2313: Type parameter 'K' has a circular constraint.
4+
tests/cases/compiler/circularBaseTypes.ts(14,11): error TS2310: Type 'Y' recursively references itself as a base type.
35

46

5-
==== tests/cases/compiler/circularBaseTypes.ts (2 errors) ====
7+
==== tests/cases/compiler/circularBaseTypes.ts (4 errors) ====
68
// Repro from #38098
79

810
type M<T> = { value: T };
@@ -16,4 +18,16 @@ tests/cases/compiler/circularBaseTypes.ts(5,6): error TS2456: Type alias 'M3' ci
1618
function f(m: M3) {
1719
return m.value;
1820
}
21+
22+
// Repro from #32581
23+
24+
type X<T> = { [K in keyof T]: string } & { b: string };
25+
~~~~~~~
26+
!!! error TS2313: Type parameter 'K' has a circular constraint.
27+
!!! related TS2751 tests/cases/compiler/circularBaseTypes.ts:14:11: Circularity originates in type at this location.
28+
interface Y extends X<Y> {
29+
~
30+
!!! error TS2310: Type 'Y' recursively references itself as a base type.
31+
a: "";
32+
}
1933

tests/baselines/reference/circularBaseTypes.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ type M3 = M2[keyof M2]; // Error
88
function f(m: M3) {
99
return m.value;
1010
}
11+
12+
// Repro from #32581
13+
14+
type X<T> = { [K in keyof T]: string } & { b: string };
15+
interface Y extends X<Y> {
16+
a: "";
17+
}
1118

1219

1320
//// [circularBaseTypes.js]
@@ -27,3 +34,11 @@ interface M2 extends M<M3> {
2734
}
2835
type M3 = M2[keyof M2];
2936
declare function f(m: M3): any;
37+
type X<T> = {
38+
[K in keyof T]: string;
39+
} & {
40+
b: string;
41+
};
42+
interface Y extends X<Y> {
43+
a: "";
44+
}

tests/baselines/reference/circularBaseTypes.symbols

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,21 @@ function f(m: M3) {
2626
>m : Symbol(m, Decl(circularBaseTypes.ts, 6, 11))
2727
}
2828

29+
// Repro from #32581
30+
31+
type X<T> = { [K in keyof T]: string } & { b: string };
32+
>X : Symbol(X, Decl(circularBaseTypes.ts, 8, 1))
33+
>T : Symbol(T, Decl(circularBaseTypes.ts, 12, 7))
34+
>K : Symbol(K, Decl(circularBaseTypes.ts, 12, 15))
35+
>T : Symbol(T, Decl(circularBaseTypes.ts, 12, 7))
36+
>b : Symbol(b, Decl(circularBaseTypes.ts, 12, 42))
37+
38+
interface Y extends X<Y> {
39+
>Y : Symbol(Y, Decl(circularBaseTypes.ts, 12, 55))
40+
>X : Symbol(X, Decl(circularBaseTypes.ts, 8, 1))
41+
>Y : Symbol(Y, Decl(circularBaseTypes.ts, 12, 55))
42+
43+
a: "";
44+
>a : Symbol(Y.a, Decl(circularBaseTypes.ts, 13, 26))
45+
}
46+

tests/baselines/reference/circularBaseTypes.types

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,14 @@ function f(m: M3) {
1919
>value : any
2020
}
2121

22+
// Repro from #32581
23+
24+
type X<T> = { [K in keyof T]: string } & { b: string };
25+
>X : X<T>
26+
>b : string
27+
28+
interface Y extends X<Y> {
29+
a: "";
30+
>a : ""
31+
}
32+

0 commit comments

Comments
 (0)