microsoft / microsoft/TypeScript
Incorrect widening (constraint fallback?) during inference when union with `undefined` appears in constraint
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.4k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 117
Description
Bug Report
🔎 Search Terms
union, type inference, imprecise union generic type, generic type.
🕗 Version & Regression Information
This is reproducible on all versions of the playground.
⏯ Playground Link
Playground link with relevant code
💻 Code
export interface Something<
R extends
| {
[K in 'foo' | 'bar' | 'baz']?: {
r?: unknown;
p?: unknown;
};
}
| undefined = undefined
> {}
const o: Something<{ foo: { r: 1; p: 2 }; bar: { r: 3 } }> = {};
declare function something<M extends T extends Something<infer R> ? keyof R : never, T extends Something>(
key: M,
something: T
): M;
declare function somethingBugged<M extends T extends Something<infer R> ? keyof R : never, T extends Something>(
key: M,
something: T
): [M];
const normal = something('foo', o);
// got: "foo"
// expected: "foo"
const bugged = somethingBugged('foo', o);
// got: ["foo" | "bar"]
// expected: ["foo"]
// However with `as const`, the correct type is returned.
const ugly = somethingBugged('foo' as const, o);
// got: ["foo"]
// expected: ["foo"]
🙁 Actual behavior
The type loses its precision and is the union instead of the exact type.
🙂 Expected behavior
The type is exact.
What is strange is that the generic type M loses precision ("foo" -> "foo" | "bar") with the somethingBugged function which returns a tuple of M (and it does the same thing with any other "wrapper", such as an object).
Whereas with the something function there is no problem. Both functions are identical.
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 example and compare inference for something and somethingBugged, especially when the inferred type is wrapped in a tuple or object. Investigate why M widens from "foo" to "foo" | "bar" only in the wrapped return type. Done means the wrapped form preserves the exact "foo" type without requiring as const.
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
- 25/100