diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 13dac76261bc7..118c217d80002 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1664,6 +1664,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getResolvedSignature: (node, candidatesOutArray, argumentCount) => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.Normal), getCandidateSignaturesForStringLiteralCompletions, getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, argumentCount) => runWithoutResolvedSignatureCaching(node, () => getResolvedSignatureWorker(node, candidatesOutArray, argumentCount, CheckMode.IsForSignatureHelp)), + getContextualOriginTypeForStringDefinition: (node: StringLiteralLike) => { + const contextualType = getContextualType(node, /*contextFlags*/ undefined); + return contextualType && contextualType.flags & TypeFlags.Union ? (contextualType as UnionType).origin : undefined; + }, getExpandedParameters, hasEffectiveRestParameter, containsArgumentsReference, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index bc6d608584359..20c5208bafc82 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4967,6 +4967,7 @@ export interface TypeChecker { /** @internal */ getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined; /** @internal */ getContextualTypeForArgumentAtIndex(call: CallLikeExpression, argIndex: number): Type | undefined; /** @internal */ getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute): Type | undefined; + /** @internal */ getContextualOriginTypeForStringDefinition(node: StringLiteralLike): Type | undefined; /** @internal */ isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean; /** @internal */ getTypeOfPropertyOfContextualType(type: Type, name: __String): Type | undefined; diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index d3148d5ae60f5..0cc7d0331ef6e 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -69,6 +69,7 @@ import { isPropertyName, isRightSideOfPropertyAccess, isStaticModifier, + isStringLiteralLike, isSwitchStatement, isTypeAliasDeclaration, isTypeReferenceNode, @@ -186,6 +187,16 @@ export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile }); } + if (isStringLiteralLike(node)) { + const contextualOriginType = typeChecker.getContextualOriginTypeForStringDefinition(node); + if (contextualOriginType?.isIndexType() && !contextualOriginType.type.isUnion()) { + const symbol = contextualOriginType.type.getProperty(node.text); + if (symbol) { + return getDefinitionFromSymbol(typeChecker, symbol, node); + } + } + } + let { symbol, failedAliasResolution } = getSymbol(node, typeChecker, stopAtAlias); let fallbackNode = node; diff --git a/tests/baselines/reference/goToDefinitionStringLiteral1.baseline.jsonc b/tests/baselines/reference/goToDefinitionStringLiteral1.baseline.jsonc new file mode 100644 index 0000000000000..e017a404f6e6b --- /dev/null +++ b/tests/baselines/reference/goToDefinitionStringLiteral1.baseline.jsonc @@ -0,0 +1,24 @@ +// === goToDefinition === +// === /tests/cases/fourslash/goToDefinitionStringLiteral1.ts === +// type User = { +// <|[|name|]: string;|> +// age: number; +// }; +// +// declare function fn(obj: T, key: keyof T): void; +// +// declare const user: User; +// +// fn(user, "name/*GOTO DEF*/"); + + // === Details === + [ + { + "kind": "property", + "name": "name", + "containerName": "__type", + "isLocal": false, + "isAmbient": false, + "unverified": false + } + ] \ No newline at end of file diff --git a/tests/cases/fourslash/goToDefinitionStringLiteral1.ts b/tests/cases/fourslash/goToDefinitionStringLiteral1.ts new file mode 100644 index 0000000000000..41d3adbefcfa2 --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionStringLiteral1.ts @@ -0,0 +1,14 @@ +/// + +//// type User = { +//// name: string; +//// age: number; +//// }; +//// +//// declare function fn(obj: T, key: keyof T): void; +//// +//// declare const user: User; +//// +//// fn(user, "name/*1*/"); + +verify.baselineGoToDefinition("1");