diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 51c9493b61721..6bb09143260e8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19317,7 +19317,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const symbolName = getFullyQualifiedName((indexType as UniqueESSymbolType).symbol, accessExpression); errorInfo = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, "[" + symbolName + "]", typeToString(objectType)); } - else if (indexType.flags & TypeFlags.StringLiteral) { + // Handle literals, type parameters (like K extends string), and keyof types + else if (indexType.flags & (TypeFlags.StringLiteral | TypeFlags.TypeParameter | TypeFlags.Index)) { + // 1. Try specific property (only for literals) + if (indexType.flags & TypeFlags.StringLiteral) { + const prop = getPropertyOfType(objectType, escapeLeadingUnderscores((indexType as StringLiteralType).value)); + if (prop) return getTypeOfSymbol(prop); + } + + // 2. Fallback to string index signature for anything string-like + if (isTypeAssignableTo(indexType, stringType)) { + const indexInfo = getIndexInfoOfType(objectType, stringType); + if (indexInfo) return indexInfo.type; + } + errorInfo = chainDiagnosticMessages(/*details*/ undefined, Diagnostics.Property_0_does_not_exist_on_type_1, (indexType as StringLiteralType).value, typeToString(objectType)); } else if (indexType.flags & TypeFlags.NumberLiteral) { diff --git a/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.js b/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.js new file mode 100644 index 0000000000000..ef63bad5df276 --- /dev/null +++ b/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.js @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts] //// + +//// [stringLiteralIndexingWithIndexSignature.ts] +interface Dic { + [key: string]: T; +} + +function getVal(obj: Dic, key: K) { + // This is the classic failure point for #30408 + // It should resolve to 'T', but often resolves to 'any' or errors + return obj[key]; +} + +type Specific = Dic["some_literal"]; // Should be 'number' + +//// [stringLiteralIndexingWithIndexSignature.js] +function getVal(obj, key) { + // This is the classic failure point for #30408 + // It should resolve to 'T', but often resolves to 'any' or errors + return obj[key]; +} diff --git a/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.symbols b/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.symbols new file mode 100644 index 0000000000000..2e7eda8120579 --- /dev/null +++ b/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.symbols @@ -0,0 +1,33 @@ +//// [tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts] //// + +=== stringLiteralIndexingWithIndexSignature.ts === +interface Dic { +>Dic : Symbol(Dic, Decl(stringLiteralIndexingWithIndexSignature.ts, 0, 0)) +>T : Symbol(T, Decl(stringLiteralIndexingWithIndexSignature.ts, 0, 14)) + + [key: string]: T; +>key : Symbol(key, Decl(stringLiteralIndexingWithIndexSignature.ts, 1, 5)) +>T : Symbol(T, Decl(stringLiteralIndexingWithIndexSignature.ts, 0, 14)) +} + +function getVal(obj: Dic, key: K) { +>getVal : Symbol(getVal, Decl(stringLiteralIndexingWithIndexSignature.ts, 2, 1)) +>T : Symbol(T, Decl(stringLiteralIndexingWithIndexSignature.ts, 4, 16)) +>K : Symbol(K, Decl(stringLiteralIndexingWithIndexSignature.ts, 4, 18)) +>obj : Symbol(obj, Decl(stringLiteralIndexingWithIndexSignature.ts, 4, 37)) +>Dic : Symbol(Dic, Decl(stringLiteralIndexingWithIndexSignature.ts, 0, 0)) +>T : Symbol(T, Decl(stringLiteralIndexingWithIndexSignature.ts, 4, 16)) +>key : Symbol(key, Decl(stringLiteralIndexingWithIndexSignature.ts, 4, 49)) +>K : Symbol(K, Decl(stringLiteralIndexingWithIndexSignature.ts, 4, 18)) + + // This is the classic failure point for #30408 + // It should resolve to 'T', but often resolves to 'any' or errors + return obj[key]; +>obj : Symbol(obj, Decl(stringLiteralIndexingWithIndexSignature.ts, 4, 37)) +>key : Symbol(key, Decl(stringLiteralIndexingWithIndexSignature.ts, 4, 49)) +} + +type Specific = Dic["some_literal"]; // Should be 'number' +>Specific : Symbol(Specific, Decl(stringLiteralIndexingWithIndexSignature.ts, 8, 1)) +>Dic : Symbol(Dic, Decl(stringLiteralIndexingWithIndexSignature.ts, 0, 0)) + diff --git a/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.types b/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.types new file mode 100644 index 0000000000000..5a0684f4919b2 --- /dev/null +++ b/tests/baselines/reference/stringLiteralIndexingWithIndexSignature.types @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts] //// + +=== stringLiteralIndexingWithIndexSignature.ts === +interface Dic { + [key: string]: T; +>key : string +> : ^^^^^^ +} + +function getVal(obj: Dic, key: K) { +>getVal : (obj: Dic, key: K) => T +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^ +>obj : Dic +> : ^^^^^^ +>key : K +> : ^ + + // This is the classic failure point for #30408 + // It should resolve to 'T', but often resolves to 'any' or errors + return obj[key]; +>obj[key] : T +> : ^ +>obj : Dic +> : ^^^^^^ +>key : K +> : ^ +} + +type Specific = Dic["some_literal"]; // Should be 'number' +>Specific : number +> : ^^^^^^ + diff --git a/tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts b/tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts new file mode 100644 index 0000000000000..f588e506559a6 --- /dev/null +++ b/tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts @@ -0,0 +1,12 @@ +// @noImplicitAny: true +interface Dic { + [key: string]: T; +} + +function getVal(obj: Dic, key: K) { + // This is the classic failure point for #30408 + // It should resolve to 'T', but often resolves to 'any' or errors + return obj[key]; +} + +type Specific = Dic["some_literal"]; // Should be 'number' \ No newline at end of file