Skip to content

Instantiation expressions should filter overloads by type parameter constraintsΒ #63126

Open

Description

πŸ”Ž Search Terms

call signature constructor signature type parameter

πŸ•— Version & Regression Information

Tested in v4.7.4, v5.9.3 and Nightly (v6.0.0-dev.20260204)
Syntax error before v4.7.4

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.9.3#code/LAKAJgpgxgNghgJwgAigewHYGcAuyBmAXMgN6jIXIA8AKgHwAUAHsTQJSsDc5lGEA7rWQQmOCBjBZkaAEYAraDkYtk7LqAC+3EKHTY8cZAF4CVEshnEAjMg11tAegeVkAPQD8oXZlwXjpq3tQJxcPYOcaAE8ABxQAcgwAVwBbGQgEOOQwNAgpDDQ8LDgcAEssfEjkHAALFD1cBDgSjDw42QUoHDigA

πŸ’» Code

declare const f: {
    <T>(x: T): T;
    new<T extends object>(x: T): T;
};

const a = f<{ b: 1 }>;
//    ^? const a: { (x: { b: 1; }): { b: 1; }; new (x: { b: 1; }): { b: 1; }; }

const b = f<1>;
//    ^? const b: { (x: 1): 1; new <T extends object>(x: T): T; }
// Type 'number' does not satisfy the constraint 'object'

πŸ™ Actual behavior

The type argument I pass to the object must met the requirements of both signatures

πŸ™‚ Expected behavior

The incompatible signature should just be filtered out, leaving only the compatible one

const b = f<1>;
//    ^? const b: (x: number) => number

Additional information about the issue

No response

Activity

  1. snarbles2 commented on Feb 11, 2026

    @snarbles2

    I see the appeal, but I'd be worried about creating an unintended situation where members are silently disappearing. One could also take the position that if an argument is permissible, then it shouldn't be a constraint.

    declare const f: {
        <T>(x: T): T;
        new<T>(x: T): T extends object ? T : never;
        // Or if yo don't like `never`, substitute preferred error sentinel
    };
  2. AFatNiBBa commented on Feb 11, 2026

    @AFatNiBBa
    Author

    I get what you're saying, but the two signatures are separate, so each has its own constraint.
    Example:

    const f: {
        <T>(x: T): T;
        new<T extends object>(x: T): T;
    } = function FuncName<T>(x: T) { return x; } as any;
    
    const a = f(1);
    //    ^? const a: 1
    // Runtime value: 1
    
    const b = f(2);
    //    ^? const b: 2
    // Runtime value: 2
    
    const c = f({ a: 1 });
    //    ^? const c: { a: number; }
    // Runtime value: { a: 1 }
    
    const d = new f(1);
    // Error: Argument of type 'number' is not assignable to parameter of type 'object'
    // Runtime value: FuncName { }
    
    const e = new f({ a: 1 });
    //    ^? const e: { a: number; }
    // Runtime value: { a: 1 }

    As you can see in d, when a constructor returns a value which neither an object nor a function, it gets ignored and an actual instance of the constructor is made at runtime.
    As you can see in a and b, this only happens when using the function as a constructor, so I would like to express this fact in the type of f

  3. AFatNiBBa commented on Feb 11, 2026

    @AFatNiBBa
    Author

    Also notice that your workaround doesn't work if the function is not called directly

    declare const original: {
        <T>(x: T): T;
        new<T extends object>(x: T): T;
    };
    
    declare const workaround: {
        <T>(x: T): T;
        new<T>(x: T): T extends object ? T : never;
    };
    
    declare const noCtor: {
        <T>(x: T): T;
        // Let's pretend there's no constructor signature because the type parameter constraint was unmet
    };
    
    const list = [ [1, 2], [3] ];
    
    const a = list.flatMap(original);
    //    ^? const a: unknown[]
    
    const b = list.flatMap(workaround);
    //    ^? const b: unknown[]
    
    const c = list.flatMap(noCtor);
    //    ^? const c: number[]

    Even if, probably, noCtor should have worked anyway, since Array.flatMap() doesn't accept constructors: There's no reason for it to get confused

  4. jcalz commented on Feb 12, 2026

    @jcalz
    Contributor

    This issue is specifically about instantiation expressions as implemented in #47607. The behavior is apparently as intended, according to #47607 (comment), so this is more like a missing feature than a bug.

    Duplicate of #51694.

  5. AFatNiBBa commented on Feb 13, 2026

    @AFatNiBBa
    Author

    I think it is a bug, the documentation states that what I'm asking for should work
    Image
    To my understanding, it is because it just checks for the number of type arguments, but that's not the expected thing to happen

  6. RyanCavanaugh commented on Feb 13, 2026

    @RyanCavanaugh
    Member

    I think the docs mean compatible by arity only. I'm not sure why it works this way, though

  7. changed the title [-]Overloads should be filtered by unmet constraints of type parameters[/-] [+]Instantiation expressions should filter overloads by type parameter constraints[/+] on Feb 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions