microsoft / microsoft/TypeScript
{} and { [K in never]: any } exhibit different simplification behavior
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.3k
- Avg merge
- 2d 4h
- Merged PRs (30d)
- 132
Description
Bug Report
Under some circumstances, TypeScript treats { [K in never]: any } & { foo?: number } differently than {} & { foo?: number }. However, this does not occur with { [K in never]: any } & { foo: number }.
🔎 Search Terms
empty mapped object type, intersection with optional object, K in never
🕗 Version & Regression Information
Tested on 4.0.5, 4.1.5, 4.2.0-beta, and the current nightly
⏯ Playground Link
💻 Code
type AreAllStringsTs<T> = string extends T ? true : false;
type Test1 = AreAllStringsTs<{ foo?: number }>; // expected: false, actual: false
type Test2 = AreAllStringsTs<{ foo: number }>; // expected: false, actual: false
type Test3 = AreAllStringsTs<string | number>; // expected: true, actual: true
type Test4 = AreAllStringsTs<{ [K in never]: any } & { foo?: number }>; // expected: false, actual: true <-- FAIL
type Test5 = AreAllStringsTs<{ [K in never]: any } & { foo: number }>; // expected: false, actual: false
🙁 Actual behavior
Test4 is true.
🙂 Expected behavior
Test4 is false.
Potential Workaround
If you have a mapped object type in which the type expression for the keys might reduce to never, add a conditional check to see if the keys set is empty. For example, I encountered this when working with a type that would make all keys of an object that could take on the value undefined optional:
type Optionalize<T> =
{ [K in RequiredKeys<T>]: T[K] }
& { [K in NonRequiredKeys<T>]?: T[K] };
Adding a check for never made the behavior consistent with what I was expecting when every field of the input type accepted undefined:
type Optionalize<T> =
(RequiredKeys<T> extends never ? {} : { [K in RequiredKeys<T>]: T[K] })
& { [K in NonRequiredKeys<T>]?: T[K]; };
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 provided Playground link and reproduce the difference between Test4 and Test5. Trace how the compiler simplifies the empty mapped type when it is intersected with optional and required object types. Done means Test4 evaluates to false while the other reported expectations remain unchanged.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100