microsoft / microsoft/TypeScript
`keyof T['prop']` becomes never when the object contains a context-sensitive function
Open
Nobody has claimed this yet.
Bug
Domain: Indexed Access Types
Help Wanted
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 117
Description
Bug Report
🔎 Search Terms
Not sure how to put this but:
- recursive type
- function
- parameter
- generics
🕗 Version & Regression Information
Tested on these versions:
- typescript@5.1.3
- typescript@5.2.0-dev.20230716 dev
⏯ Playground Link
Code
type Type<
TAcceptedKeys extends string,
TType extends Type<TAcceptedKeys>=any
> = {
/**
* We're using the keys of this property, that is why
* we use a recursive type.
*/
obj?: Record<TAcceptedKeys, 1>
/**
* Just for the sake of illustration.
* In the circumstances of the bug this will be never[].
*/
keys: (keyof TType['obj'])[]
/**
* A function with a parameter inside the recursive type.
* This triggers the bug.
*/
triggerFn?: (param: number) => void
/**
* This will always be TType, regardless of whether
* a parameter is declared in `triggerFn` or not.
*/
shouldBeTType?: TType
/**
* This will be `keyof TType` as expected if a parameter
* is not declared in triggerFn, but it will turn to `undefined`
* as soon as a parameter is declared in that function.
*
* If the below property is passed to defineType
* the type will be as expected:
* triggerFn: () => {}
*
* However this will trigger the bug:
* triggerFn: (param) => {}
*/
shouldBeKeyofTType?: keyof TType
}
const defineType = <TAcceptedKeys extends string>() =>
<const TType extends Type<TAcceptedKeys, TType>>(type: TType) => type
defineType<'apple' | 'banana'>()({
obj: {
apple: 1,
banana: 1
},
keys: [
'apple'
],
/**
* Toggle these comments to see how the bug works.
*/
triggerFn: () => {}, // won't produce diagnostics
// triggerFn: (param) => {}, // type 'string' is not assignable to type 'never'
/**
* Toggle these comments to see diagnostics information.
*/
// shouldBeTType: null,
// shouldBeKeyofTType: null
})
🙁 Actual behavior
The presence or absence of a parameter in an object member function has effect on this object type in certain circumstances.
🙂 Expected behavior
It shouldn't.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the linked TypeScript Playground and reproduce the recursive Type<TAcceptedKeys, TType> example. Compare the inferred types and diagnostics when triggerFn has no parameter versus a parameter, especially for keys and shouldBeKeyofTType. Done means the function parameter no longer changes the resulting keyof types unexpectedly.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100