Skip to content
Open
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
15 changes: 14 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//// [tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts] ////

//// [stringLiteralIndexingWithIndexSignature.ts]
interface Dic<T> {
[key: string]: T;
}

function getVal<T, K extends string>(obj: Dic<T>, 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<number>["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];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//// [tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts] ////

=== stringLiteralIndexingWithIndexSignature.ts ===
interface Dic<T> {
>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<T, K extends string>(obj: Dic<T>, 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<number>["some_literal"]; // Should be 'number'
>Specific : Symbol(Specific, Decl(stringLiteralIndexingWithIndexSignature.ts, 8, 1))
>Dic : Symbol(Dic, Decl(stringLiteralIndexingWithIndexSignature.ts, 0, 0))

Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//// [tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts] ////

=== stringLiteralIndexingWithIndexSignature.ts ===
interface Dic<T> {
[key: string]: T;
>key : string
> : ^^^^^^
}

function getVal<T, K extends string>(obj: Dic<T>, key: K) {
>getVal : <T, K extends string>(obj: Dic<T>, key: K) => T
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^^^^^
>obj : Dic<T>
> : ^^^^^^
>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<T>
> : ^^^^^^
>key : K
> : ^
}

type Specific = Dic<number>["some_literal"]; // Should be 'number'
>Specific : number
> : ^^^^^^

12 changes: 12 additions & 0 deletions tests/cases/compiler/stringLiteralIndexingWithIndexSignature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @noImplicitAny: true
interface Dic<T> {
[key: string]: T;
}

function getVal<T, K extends string>(obj: Dic<T>, 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<number>["some_literal"]; // Should be 'number'