Description
🔎 Search Terms
"tsserver", "generic object completion", "generic intellisense", "generic typeahead", "generic completion", "generic object properties", "object property completion", "typescript server", "typescript lsp"
🕗 Version & Regression Information
This is the behavior in every version I tried, and I reviewed the FAQ for entries about typeahead support for generic type arguments.
⏯ Playground Link
💻 Code
type TypeA<T extends 'one' | 'two'> = T
type TypeB<T extends { prop: 'one' | 'two' }> = T
// ✅ Good: Completions provided
type ExampleA = TypeA<''>
// ❌ Bad: Completions NOT provided
type ExampleB = TypeB<{ prop: '' }>
🙁 Actual behavior
Getting completions between the quotes on line 5 (using Control + Space
on Mac OS) correctly provides 'one'
and 'two'
as options.

However, doing the same between the quotes on line 8 provides no completions.

🙂 Expected behavior
Getting completions between the quotes on both lines 5 and 8 (using Control + Space
on Mac OS) correctly provides 'one'
and 'two'
as options.
Additional information about the issue
The type of prop
is correct ('one' | 'two'
) and if you enter an invalid value you get the correct type error. Therefore, TypeScript should have sufficient information to provide the exact same completions in both examples.

I enabled verbose tsserver
logging in Visual Studio Code and captured the requests and responses for these completion requests.
Line 5 Request:
{
"seq":12,
"type":"request",
"command":"completionInfo",
"arguments":{
"file":"/Users/ryan.palmer/Desktop/Test.ts",
"line":5,
"offset":24,
"includeExternalModuleExports":true,
"includeInsertTextCompletions":true,
"triggerKind":1
}
}
Line 5 Response:
{
"seq":0,
"type":"response",
"command":"completionInfo",
"request_seq":12,
"success":true,
"body":{
"isGlobalCompletion":false,
"isMemberCompletion":false,
"isNewIdentifierLocation":false,
"optionalReplacementSpan":{
"start":{
"line":5,
"offset":24
},
"end":{
"line":5,
"offset":24
}
},
"entries":[
{
"name":"one",
"kind":"string",
"kindModifiers":"",
"sortText":"11",
"replacementSpan":{
"start":{
"line":5,
"offset":24
},
"end":{
"line":5,
"offset":24
}
}
},
{
"name":"two",
"kind":"string",
"kindModifiers":"",
"sortText":"11",
"replacementSpan":{
"start":{
"line":5,
"offset":24
},
"end":{
"line":5,
"offset":24
}
}
}
]
}
}
Line 8 Request:
{
"seq":9,
"type":"request",
"command":"completionInfo",
"arguments":{
"file":"/Users/ryan.palmer/Desktop/Test.ts",
"line":8,
"offset":32,
"includeExternalModuleExports":true,
"includeInsertTextCompletions":true,
"triggerKind":1
}
}
Line 8 Response:
{
"seq":0,
"type":"response",
"command":"completionInfo",
"request_seq":9,
"success":false,
"message":"No content available."
}
Line 8 Logs:
Info 451 [17:32:09.801] getCompletionData: Get current token: 0.018829017877578735
Info 452 [17:32:09.801] getCompletionData: Is inside comment: 0.007900983095169067
Info 453 [17:32:09.801] getCompletionData: Get previous token: 0.10709202289581299
Info 454 [17:32:09.802] getCompletionsAtPosition: isCompletionListBlocker: 0.03820300102233887
Info 455 [17:32:09.802] Returning an empty list because completion was requested in an invalid position.
As you can see, the requests are both identical except for position. But the response for the request on line 8 is empty, and there is a message indicating the position is invalid.