microsoft / microsoft/TypeScript
Constrained generic types infer `never` on a function, even when the argument meets the constraints
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
- inference of generic types fails with typed argument
- infer generic never
- constrained generic infers never
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about 4.3.2
⏯ Playground Link
Playground link with relevant code
💻 Code
type ValidKey = string | number | symbol;
/* The class ultimately being built */
class Test<T, I extends ValidKey & keyof T> {
idProp: I;
constructor(idProp: I) {
this.idProp = idProp;
}
}
interface WithId<T, I extends ValidKey & keyof T> {
idProp: I;
}
type IDableEntity<I extends ValidKey> = Record<I, ValidKey>;
class TestBuilder<T extends Record<ValidKey, any>> {
id<I extends ValidKey & keyof T>(idProp: I): this & WithId<T, I> {
return {
...this,
"idProp": idProp
};
}
}
function testBuilder<T>(): TestBuilder<T> {
return new TestBuilder<T>();
}
function build<
T extends IDableEntity<I>,
I extends ValidKey & keyof T>
(builder: TestBuilder<T> & WithId<T, I>): Test<T, I> {
return new Test<T, I>(builder.idProp);
}
interface IDable {
id: string;
}
const foo: TestBuilder<IDable> & WithId<IDable, 'id'> = testBuilder<IDable>().id('id');
const bar = build(foo);
/*
Argument of type 'TestBuilder<IDable> & WithId<IDable, "id">' is not assignable to parameter of type 'TestBuilder<IDableEntity<never>> & WithId<IDableEntity<never>, never>'.
Type 'TestBuilder<IDable> & WithId<IDable, "id">' is not assignable to type 'WithId<IDableEntity<never>, never>'.
Types of property 'idProp' are incompatible.
Type 'string' is not assignable to type 'never'.ts(2345)
*/
This also fails if I create a new type CompleteTestBuilder<T extends IDableEntity<I>, I extends ValidKey & keyof T> = TestBuilder<T> & WithId<T, I> that build accepts as an argument, or if I define CompleteTestBuilder as its own interface extends TestBuilder<T> { idProp: I}.
A simpler version that does NOT fail:
function build2<T>(builder: TestBuilder<T>): TestBuilder<T> {
return builder;
}
const baz: TestBuilder<IDable> = testBuilder<IDable>();
const qux = build2(baz); // qux: TestBuilder<IDable>
This version also does NOT fail:
function build3<T extends Record<ValidKey, any>>(builder: TestBuilder<T>): (t: T) => string {
return (t: T) => 'boo';
}
const quux = build3(baz); // quux: (t: IDable) => string
🙁 Actual behavior
The build call fails with the error message:
Argument of type 'TestBuilder<IDable> & WithId<IDable, "id">' is not assignable to parameter of type 'TestBuilder<IDableEntity<never>> & WithId<IDableEntity<never>, never>'.
Type 'TestBuilder<IDable> & WithId<IDable, "id">' is not assignable to type 'WithId<IDableEntity<never>, never>'.
Types of property 'idProp' are incompatible.
Type 'string' is not assignable to type 'never'.ts(2345)
🙂 Expected behavior
I would expect the build call to succeed, and types T and I to be inferred as IDable and "id", respectively. The argument type is already known and correct (it is even inferred correctly - the type assignment for foo is just to show that the type is correct). The types on the function should be able to match.
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 Playground reproduction and the generic inference behavior in the build function. Trace how the constraints on T and I produce never, then verify that the build(foo) call succeeds and infers T as IDable and I as "id" without breaking the simpler examples.
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
- 30/100