Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8661,6 +8661,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}

function symbolToTypeNode(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags, overrideTypeArguments?: readonly TypeNode[]): TypeNode {
// Prevent infinite recursion when a function's return type references itself (e.g., ReturnType<typeof clone>)
if (!context.visitedSymbols) {
context.visitedSymbols = new Set();
}
const symbolKey = `${getSymbolId(symbol)}|${meaning}`;
if (context.visitedSymbols.has(symbolKey)) {
// Detected recursive symbol reference, return never type to avoid crash
return factory.createKeywordTypeNode(SyntaxKind.NeverKeyword);
}
context.visitedSymbols.add(symbolKey);

const chain = lookupSymbolChain(symbol, context, meaning, !(context.flags & NodeBuilderFlags.UseAliasDefinedOutsideCurrentScope)); // If we're using aliases outside the current scope, dont bother with the module

const isTypeOf = meaning === SymbolFlags.Value;
Expand Down Expand Up @@ -8723,33 +8734,39 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
const lit = factory.createLiteralTypeNode(factory.createStringLiteral(specifier));
context.approximateLength += specifier.length + 10; // specifier + import("")
let result: TypeNode;
if (!nonRootParts || isEntityName(nonRootParts)) {
if (nonRootParts) {
const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right;
setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined);
}
return factory.createImportTypeNode(lit, attributes, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf);
result = factory.createImportTypeNode(lit, attributes, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf);
}
else {
const splitNode = getTopmostIndexedAccessType(nonRootParts);
const qualifier = (splitNode.objectType as TypeReferenceNode).typeName;
return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, attributes, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType);
result = factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, attributes, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType);
}
context.visitedSymbols.delete(symbolKey);
return result;
}

const entityName = createAccessFromSymbolChain(chain, chain.length - 1, 0);
let result: TypeNode;
if (isIndexedAccessTypeNode(entityName)) {
return entityName; // Indexed accesses can never be `typeof`
result = entityName; // Indexed accesses can never be `typeof`
}
if (isTypeOf) {
return factory.createTypeQueryNode(entityName);
else if (isTypeOf) {
result = factory.createTypeQueryNode(entityName);
}
else {
const lastId = isIdentifier(entityName) ? entityName : entityName.right;
const lastTypeArgs = getIdentifierTypeArguments(lastId);
setIdentifierTypeArguments(lastId, /*typeArguments*/ undefined);
return factory.createTypeReferenceNode(entityName, lastTypeArgs as NodeArray<TypeNode>);
result = factory.createTypeReferenceNode(entityName, lastTypeArgs as NodeArray<TypeNode>);
}
context.visitedSymbols.delete(symbolKey);
return result;

function createAccessFromSymbolChain(chain: Symbol[], index: number, stopper: number): EntityName | IndexedAccessTypeNode {
const typeParameterNodes = index === (chain.length - 1) ? overrideTypeArguments : lookupTypeParameterNodes(chain, index, context);
Expand Down Expand Up @@ -53030,7 +53047,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
let current: Node = node;
while (current) {
if (isFunctionLikeOrClassStaticBlockDeclaration(current)) {
return grammarErrorOnNode(node, Diagnostics.Jump_target_cannot_cross_function_boundary);
return grammarErrorOnNode(node, Diagnostics.Jump_target_0_cannot_cross_function_boundary, node.label ? node.label.text : "");
}

switch (current.kind) {
Expand Down Expand Up @@ -54300,6 +54317,7 @@ interface NodeBuilderContext extends SyntacticTypeNodeBuilderContext {
reportedDiagnostic: boolean;
trackedSymbols: TrackedSymbol[] | undefined;
visitedTypes: Set<number> | undefined;
visitedSymbols: Set<string> | undefined;
symbolDepth: Map<string, number> | undefined;
inferTypeParameters: TypeParameter[] | undefined;
approximateLength: number;
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@
"category": "Error",
"code": 1107
},
"Jump target '{0}' cannot cross function boundary.": {
"category": "Error",
"code": 1107
},
"A 'return' statement can only be used within a function body.": {
"category": "Error",
"code": 1108
Expand Down