diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c5701087ebfd4..c2514af2af229 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -49700,6 +49700,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function getParameterSymbolFromJSDocHost(node: Identifier): Symbol | undefined { + if (!isIdentifier(node)) { + return undefined; + } + const name = node.escapedText; + const decl = getHostSignatureFromJSDoc(node); + if (!decl) { + return undefined; + } + const parameter = find(decl.parameters, p => p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); + if (parameter && parameter.symbol) { + return parameter.symbol; + } + // If parameter.symbol is not available, try to get it from the function's locals + if (parameter && decl.locals) { + return decl.locals.get(name); + } + return undefined; + } + function getSymbolOfNameOrPropertyAccessExpression(name: EntityName | PrivateIdentifier | PropertyAccessExpression | JSDocMemberName): Symbol | undefined { if (isDeclarationName(name)) { return getSymbolOfNode(name.parent); @@ -49786,20 +49806,28 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const isJSDoc = findAncestor(name, or(isJSDocLinkLike, isJSDocNameReference, isJSDocMemberName)); - const meaning = isJSDoc ? SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value : SymbolFlags.Value; + const isJSDocContext = !!(name.flags & NodeFlags.JSDoc) || isJSDoc; + const meaning = isJSDocContext ? SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Value : SymbolFlags.Value; if (name.kind === SyntaxKind.Identifier) { if (isJSXTagName(name) && isJsxIntrinsicTagName(name)) { const symbol = getIntrinsicTagSymbol(name.parent as JsxOpeningLikeElement); return symbol === unknownSymbol ? undefined : symbol; } + // Check for parameter symbol in JSDoc typeof context + if (isJSDocContext && isInTypeQuery(name)) { + const parameterSymbol = getParameterSymbolFromJSDocHost(name); + if (parameterSymbol) { + return parameterSymbol; + } + } const result = resolveEntityName(name, meaning, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, getHostSignatureFromJSDoc(name)); - if (!result && isJSDoc) { + if (!result && isJSDocContext) { const container = findAncestor(name, or(isClassLike, isInterfaceDeclaration)); if (container) { return resolveJSDocMemberName(name, /*ignoreErrors*/ true, getSymbolOfDeclaration(container)); } } - if (result && isJSDoc) { + if (result && isJSDocContext) { const container = getJSDocHost(name); if (container && isEnumMember(container) && container === result.valueDeclaration) { return resolveEntityName(name, meaning, /*ignoreErrors*/ true, /*dontResolveAlias*/ true, getSourceFileOfNode(container)) || result; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4687bb8796fad..016942602510d 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -11829,6 +11829,13 @@ export function createNameResolver({ return undefined; } if (!result) { + // Check for parameter symbol in JSDoc typeof context before failing + if (originalLocation && !isString(nameArg) && (originalLocation.flags & NodeFlags.JSDoc) && isInTypeQuery(originalLocation)) { + const parameterSymbol = getParameterSymbolFromJSDocHost(originalLocation, (nameArg as Identifier).escapedText); + if (parameterSymbol) { + return parameterSymbol; + } + } onFailedToResolveSymbol(originalLocation, nameArg, meaning, nameNotFoundMessage); } else { @@ -11839,6 +11846,25 @@ export function createNameResolver({ return result; } + function getParameterSymbolFromJSDocHost(node: Node, name: __String): Symbol | undefined { + const decl = getHostSignatureFromJSDoc(node); + if (!decl) { + return undefined; + } + const parameter = find(decl.parameters, p => p.name && p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name); + if (parameter && parameter.symbol) { + return parameter.symbol; + } + // If parameter.symbol is not available, try to get it from the function's locals + if (parameter && decl.locals) { + const localSymbol = decl.locals.get(name); + if (localSymbol) { + return localSymbol; + } + } + return undefined; + } + function useOuterVariableScopeInParameter(result: Symbol, location: Node, lastLocation: Node) { const target = getEmitScriptTarget(compilerOptions); const functionLocation = location as FunctionLikeDeclaration; diff --git a/tests/baselines/reference/jsdocTypeofParameterConsistency.symbols b/tests/baselines/reference/jsdocTypeofParameterConsistency.symbols new file mode 100644 index 0000000000000..4bdc461217990 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeofParameterConsistency.symbols @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/jsdocTypeofParameterConsistency.ts] //// + +=== jsdocTypeofParameterConsistency.js === +/** + * @template T + * @param {T} a + * @return {typeof a} + */ +function f(a) { +>f : Symbol(f, Decl(jsdocTypeofParameterConsistency.js, 0, 0)) +>a : Symbol(a, Decl(jsdocTypeofParameterConsistency.js, 5, 11)) + + return a; +>a : Symbol(a, Decl(jsdocTypeofParameterConsistency.js, 5, 11)) +} + +/** + * @template T + * @param {T} b + * @return {typeof b} + */ +function g(b) { +>g : Symbol(g, Decl(jsdocTypeofParameterConsistency.js, 7, 1)) +>b : Symbol(b, Decl(jsdocTypeofParameterConsistency.js, 14, 11)) + + return b; +>b : Symbol(b, Decl(jsdocTypeofParameterConsistency.js, 14, 11)) +} diff --git a/tests/baselines/reference/jsdocTypeofParameterConsistency.types b/tests/baselines/reference/jsdocTypeofParameterConsistency.types new file mode 100644 index 0000000000000..9baf2bdaba2d2 --- /dev/null +++ b/tests/baselines/reference/jsdocTypeofParameterConsistency.types @@ -0,0 +1,34 @@ +//// [tests/cases/compiler/jsdocTypeofParameterConsistency.ts] //// + +=== jsdocTypeofParameterConsistency.js === +/** + * @template T + * @param {T} a + * @return {typeof a} + */ +function f(a) { +>f : (a: T) => typeof a +> : ^ ^^ ^^ ^^^^^ +>a : T +> : ^ + + return a; +>a : T +> : ^ +} + +/** + * @template T + * @param {T} b + * @return {typeof b} + */ +function g(b) { +>g : (b: T) => typeof b +> : ^ ^^ ^^ ^^^^^ +>b : T +> : ^ + + return b; +>b : T +> : ^ +} diff --git a/tests/baselines/reference/jsdocTypeofParameterResolution.baseline.jsonc b/tests/baselines/reference/jsdocTypeofParameterResolution.baseline.jsonc new file mode 100644 index 0000000000000..4b1b59a61a45d --- /dev/null +++ b/tests/baselines/reference/jsdocTypeofParameterResolution.baseline.jsonc @@ -0,0 +1,286 @@ +// === findAllReferences === +// === /tests/cases/fourslash/jsdocTypeofParameterResolution.ts === +// /** +// * @template T +// * @param {T} /*FIND ALL REFS*/[|a|] +// * @return {typeof [|a|]} +// */ +// function f([|{| isWriteAccess: true |}a|]) { +// return [|a|]; +// } +// +// let a = 123; +// --- (line: 11) skipped --- + + // === Definitions === + // === /tests/cases/fourslash/jsdocTypeofParameterResolution.ts === + // /** + // * @template T + // * @param {T} /*FIND ALL REFS*/a + // * @return {typeof a} + // */ + // function f([|a|]) { + // return a; + // } + // + // --- (line: 10) skipped --- + + // === Details === + [ + { + "containerKind": "", + "containerName": "", + "kind": "parameter", + "name": "(parameter) a: any", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "parameter", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "a", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + } + ] + } + ] + + + +// === findAllReferences === +// === /tests/cases/fourslash/jsdocTypeofParameterResolution.ts === +// /** +// * @template T +// * @param {T} [|a|] +// * @return {typeof /*FIND ALL REFS*/[|a|]} +// */ +// function f([|{| isWriteAccess: true |}a|]) { +// return [|a|]; +// } +// +// let a = 123; +// --- (line: 11) skipped --- + + // === Definitions === + // === /tests/cases/fourslash/jsdocTypeofParameterResolution.ts === + // /** + // * @template T + // * @param {T} a + // * @return {typeof /*FIND ALL REFS*/a} + // */ + // function f([|a|]) { + // return a; + // } + // + // --- (line: 10) skipped --- + + // === Details === + [ + { + "containerKind": "", + "containerName": "", + "kind": "parameter", + "name": "(parameter) a: any", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "parameter", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "a", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + } + ] + } + ] + + + +// === findAllReferences === +// === /tests/cases/fourslash/jsdocTypeofParameterResolution.ts === +// --- (line: 10) skipped --- +// +// /** +// * @template T +// * @param {T} /*FIND ALL REFS*/[|a|] +// * @return {typeof [|a|]} +// */ +// function g([|{| isWriteAccess: true |}a|]) { +// return [|a|]; +// } + + // === Definitions === + // === /tests/cases/fourslash/jsdocTypeofParameterResolution.ts === + // --- (line: 10) skipped --- + // + // /** + // * @template T + // * @param {T} /*FIND ALL REFS*/a + // * @return {typeof a} + // */ + // function g([|a|]) { + // return a; + // } + + // === Details === + [ + { + "containerKind": "", + "containerName": "", + "kind": "parameter", + "name": "(parameter) a: any", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "parameter", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "a", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + } + ] + } + ] + + + +// === findAllReferences === +// === /tests/cases/fourslash/jsdocTypeofParameterResolution.ts === +// --- (line: 10) skipped --- +// +// /** +// * @template T +// * @param {T} [|a|] +// * @return {typeof /*FIND ALL REFS*/[|a|]} +// */ +// function g([|{| isWriteAccess: true |}a|]) { +// return [|a|]; +// } + + // === Definitions === + // === /tests/cases/fourslash/jsdocTypeofParameterResolution.ts === + // --- (line: 11) skipped --- + // /** + // * @template T + // * @param {T} a + // * @return {typeof /*FIND ALL REFS*/a} + // */ + // function g([|a|]) { + // return a; + // } + + // === Details === + [ + { + "containerKind": "", + "containerName": "", + "kind": "parameter", + "name": "(parameter) a: any", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "parameter", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "a", + "kind": "parameterName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "any", + "kind": "keyword" + } + ] + } + ] \ No newline at end of file diff --git a/tests/cases/compiler/jsdocTypeofParameterConsistency.ts b/tests/cases/compiler/jsdocTypeofParameterConsistency.ts new file mode 100644 index 0000000000000..ef9c74c0a57a3 --- /dev/null +++ b/tests/cases/compiler/jsdocTypeofParameterConsistency.ts @@ -0,0 +1,22 @@ +// @allowJs: true +// @checkJs: true +// @noEmit: true + +// @filename: jsdocTypeofParameterConsistency.js +/** + * @template T + * @param {T} a + * @return {typeof a} + */ +function f(a) { + return a; +} + +/** + * @template T + * @param {T} b + * @return {typeof b} + */ +function g(b) { + return b; +} \ No newline at end of file diff --git a/tests/cases/fourslash/jsdocTypeofParameterResolution.ts b/tests/cases/fourslash/jsdocTypeofParameterResolution.ts new file mode 100644 index 0000000000000..b187a3c0771fb --- /dev/null +++ b/tests/cases/fourslash/jsdocTypeofParameterResolution.ts @@ -0,0 +1,24 @@ +/// + +/////** +//// * @template T +//// * @param {T} /*1*/a +//// * @return {typeof /*2*/a} +//// */ +////function f(a) { +//// return a; +////} +//// +////let a = 123; +//// +/////** +//// * @template T +//// * @param {T} /*3*/a +//// * @return {typeof /*4*/a} +//// */ +////function g(a) { +//// return a; +////} + +// Test that typeof a in JSDoc resolves to parameter consistently +verify.baselineFindAllReferences("1", "2", "3", "4"); \ No newline at end of file pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy