Skip to content

Commit 0a8f9c0

Browse files
committed
Fix typeof parameter resolution inconsistency in JSDoc comments
1 parent efeb135 commit 0a8f9c0

7 files changed

Lines changed: 452 additions & 3 deletions

src/compiler/checker.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49630,6 +49630,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4963049630
}
4963149631
}
4963249632

49633+
function getParameterSymbolFromJSDocHost(node: Identifier): Symbol | undefined {
49634+
if (!isIdentifier(node)) {
49635+
return undefined;
49636+
}
49637+
const name = node.escapedText;
49638+
const decl = getHostSignatureFromJSDoc(node);
49639+
if (!decl) {
49640+
return undefined;
49641+
}
49642+
const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name);
49643+
if (parameter && parameter.symbol) {
49644+
return parameter.symbol;
49645+
}
49646+
// If parameter.symbol is not available, try to get it from the function's locals
49647+
if (parameter && decl.locals) {
49648+
return decl.locals.get(name);
49649+
}
49650+
return undefined;
49651+
}
49652+
49653+
4963349654
function getSymbolOfNameOrPropertyAccessExpression(name: EntityName | PrivateIdentifier | PropertyAccessExpression | JSDocMemberName): Symbol | undefined {
4963449655
if (isDeclarationName(name)) {
4963549656
return getSymbolOfNode(name.parent);
@@ -49716,20 +49737,28 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4971649737
}
4971749738

4971849739
const isJSDoc = findAncestor(name, or(isJSDocLinkLike, isJSDocNameReference, isJSDocMemberName));
49719-
const meaning = isJSDoc ? SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value : SymbolFlags.Value;
49740+
const isJSDocContext = !!(name.flags & NodeFlags.JSDoc) || isJSDoc;
49741+
const meaning = isJSDocContext ? SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value : SymbolFlags.Value;
4972049742
if (name.kind === SyntaxKind.Identifier) {
4972149743
if (isJSXTagName(name) && isJsxIntrinsicTagName(name)) {
4972249744
const symbol = getIntrinsicTagSymbol(name.parent as JsxOpeningLikeElement);
4972349745
return symbol === unknownSymbol ? undefined : symbol;
4972449746
}
49747+
// Check for parameter symbol in JSDoc typeof context
49748+
if (isJSDocContext && isInTypeQuery(name)) {
49749+
const parameterSymbol = getParameterSymbolFromJSDocHost(name);
49750+
if (parameterSymbol) {
49751+
return parameterSymbol;
49752+
}
49753+
}
4972549754
const result = resolveEntityName(name, meaning, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, getHostSignatureFromJSDoc(name));
49726-
if (!result && isJSDoc) {
49755+
if (!result && isJSDocContext) {
4972749756
const container = findAncestor(name, or(isClassLike, isInterfaceDeclaration));
4972849757
if (container) {
4972949758
return resolveJSDocMemberName(name, /*ignoreErrors*/ true, getSymbolOfDeclaration(container));
4973049759
}
4973149760
}
49732-
if (result && isJSDoc) {
49761+
if (result && isJSDocContext) {
4973349762
const container = getJSDocHost(name);
4973449763
if (container && isEnumMember(container) && container === result.valueDeclaration) {
4973549764
return resolveEntityName(name, meaning, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, getSourceFileOfNode(container)) || result;

src/compiler/utilities.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11829,6 +11829,13 @@ export function createNameResolver({
1182911829
return undefined;
1183011830
}
1183111831
if (!result) {
11832+
// Check for parameter symbol in JSDoc typeof context before failing
11833+
if (originalLocation && !isString(nameArg) && (originalLocation.flags & NodeFlags.JSDoc) && isInTypeQuery(originalLocation)) {
11834+
const parameterSymbol = getParameterSymbolFromJSDocHost(originalLocation, (nameArg as Identifier).escapedText);
11835+
if (parameterSymbol) {
11836+
return parameterSymbol;
11837+
}
11838+
}
1183211839
onFailedToResolveSymbol(originalLocation, nameArg, meaning, nameNotFoundMessage);
1183311840
}
1183411841
else {
@@ -11839,6 +11846,25 @@ export function createNameResolver({
1183911846
return result;
1184011847
}
1184111848

11849+
function getParameterSymbolFromJSDocHost(node: Node, name: __String): Symbol | undefined {
11850+
const decl = getHostSignatureFromJSDoc(node);
11851+
if (!decl) {
11852+
return undefined;
11853+
}
11854+
const parameter = find(decl.parameters, p => p.name && p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name);
11855+
if (parameter && parameter.symbol) {
11856+
return parameter.symbol;
11857+
}
11858+
// If parameter.symbol is not available, try to get it from the function's locals
11859+
if (parameter && decl.locals) {
11860+
const localSymbol = decl.locals.get(name);
11861+
if (localSymbol) {
11862+
return localSymbol;
11863+
}
11864+
}
11865+
return undefined;
11866+
}
11867+
1184211868
function useOuterVariableScopeInParameter(result: Symbol, location: Node, lastLocation: Node) {
1184311869
const target = getEmitScriptTarget(compilerOptions);
1184411870
const functionLocation = location as FunctionLikeDeclaration;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [tests/cases/compiler/jsdocTypeofParameterConsistency.ts] ////
2+
3+
=== jsdocTypeofParameterConsistency.js ===
4+
/**
5+
* @template T
6+
* @param {T} a
7+
* @return {typeof a}
8+
*/
9+
function f(a) {
10+
>f : Symbol(f, Decl(jsdocTypeofParameterConsistency.js, 0, 0))
11+
>a : Symbol(a, Decl(jsdocTypeofParameterConsistency.js, 5, 11))
12+
13+
return a;
14+
>a : Symbol(a, Decl(jsdocTypeofParameterConsistency.js, 5, 11))
15+
}
16+
17+
/**
18+
* @template T
19+
* @param {T} b
20+
* @return {typeof b}
21+
*/
22+
function g(b) {
23+
>g : Symbol(g, Decl(jsdocTypeofParameterConsistency.js, 7, 1))
24+
>b : Symbol(b, Decl(jsdocTypeofParameterConsistency.js, 14, 11))
25+
26+
return b;
27+
>b : Symbol(b, Decl(jsdocTypeofParameterConsistency.js, 14, 11))
28+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [tests/cases/compiler/jsdocTypeofParameterConsistency.ts] ////
2+
3+
=== jsdocTypeofParameterConsistency.js ===
4+
/**
5+
* @template T
6+
* @param {T} a
7+
* @return {typeof a}
8+
*/
9+
function f(a) {
10+
>f : <T>(a: T) => typeof a
11+
> : ^ ^^ ^^ ^^^^^
12+
>a : T
13+
> : ^
14+
15+
return a;
16+
>a : T
17+
> : ^
18+
}
19+
20+
/**
21+
* @template T
22+
* @param {T} b
23+
* @return {typeof b}
24+
*/
25+
function g(b) {
26+
>g : <T>(b: T) => typeof b
27+
> : ^ ^^ ^^ ^^^^^
28+
>b : T
29+
> : ^
30+
31+
return b;
32+
>b : T
33+
> : ^
34+
}

0 commit comments

Comments
 (0)