镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

TS is overly picky when declaring a class constructor type #29707

Description

@bcherny

TypeScript Version: 3.4.0-dev.20190202

Search Terms: class, extend, constructor, any[]

Code

type ClassConstructor = new(...args: any[]) => {}

function mixin<C extends ClassConstructor>(Class: C) {
  return class extends Class {}
}

Expected behavior:

I should be able to replace ...args: any[] with ...args: unknown[], or any other signature.

Actual behavior:

Error: Type 'C' is not a constructor function type. [2507]

Playground Link: https://www.typescriptlang.org/play/index.html#src=type%20ClassConstructor%20%3D%20new(...args%3A%20unknown%5B%5D)%20%3D%3E%20%7B%7D%0D%0A%0D%0Afunction%20mixin%3CC%20extends%20ClassConstructor%3E(Class%3A%20C)%20%7B%0D%0A%20%20return%20class%20extends%20Class%20%7B%7D%0D%0A%7D

Activity

  1. dragomirtitian commented on Feb 3, 2019

    @dragomirtitian
    Contributor

    In the pull request the rest parameter of any[] is required:

    In the following, the term mixin constructor type refers to a type that has a single construct signature with a single rest argument of type any[] and an object-like return type.

    Not saying it should be but this is the expected behavior, unknown along came a long time after this PR, maybe the rules can be changed.

  2. RyanCavanaugh commented on Feb 4, 2019

    @RyanCavanaugh
    Member

    Agree, unknown[] should be an acceptable substitute. Accepting PRs for an easy fix.

  3. dragomirtitian commented on Feb 4, 2019

    @dragomirtitian
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh) So the issue seems ridiculously easy to fix (and I have the code ready), I was just wondering how to go about the tests. Should I add a new one or change one to include a version with unknown[] (this seems like a good candidate conformance/classes/mixinClassesAnnotated.ts)

  4. RyanCavanaugh commented on Feb 4, 2019

    @RyanCavanaugh
    Member

    Titian Cernicova-Dragomir (@dragomirtitian) no preference; whichever's easiest for you

  5. dragomirtitian commented on Feb 4, 2019

    @dragomirtitian
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh) I added a fix, just one problem (which unfortunately probably makes my fix almost useless).

    The constraint new(...args: unknown[]) => {} works well as long as strictFunctionTypes are not turned on. When that flag is on this code becomes an error

    class Base {
        constructor(public x: number, public y: number) {}
    }
    let c : new (...args: unknown[]) => {} = Base // error under strictFunctionTypes: true
    new c("") // runtime type mismatch 

    This means that you can't really call the mixin with anything but an empty constructor or one that has unknown arguments.

  6. RyanCavanaugh commented on Feb 4, 2019

    @RyanCavanaugh
    Member

    That's why it's a constraint (not a concrete type) though, right?

  7. dragomirtitian commented on Feb 5, 2019

    @dragomirtitian
    Contributor

    Ryan Cavanaugh (@RyanCavanaugh) I can look into that, but this problem seems much bigger than mixins and would have implications broadly. Rest parameters of type unknown[] are generally not compatible with arbitrary parameter types even if we are talking about a constraint. This currently fails under strictFunctionTypes

    function test<T extends (...a: unknown[]) => unknown>(fn:T) : T{
          fn(""); // this passes "" can be assigned to unknown 
          return fn;
    }
    test(function (a: number) {   // error here under strictFunctionTypes
    })

    I'm not 100% sure it is necessarily a great idea to make the code above error free . It seems to be weaker as far as type safety goes. As we see in the code above the callback passed in accepts a number but it's called inside test with a string. I think the current behavior is better, don't let a function with a number parameter be assigned to a function with an unknown parameter, the implementing function can't handle unknown.

    While this is no worse than if we use any[], unknown[] would give a false sense of type-safety when no safety actually exists. Except for obeying the no-any tslint rule not sure why unknown is better in this instance. At least any would function as a '💀💀 careful unsafe 💀💀' warning, at least that's the way I see any :)

  8. jszopi commented on Jul 23, 2022

    @jszopi

    The problem isn't specific to constructors or rest parameters, it's just that function types are contravariant in their parameter types. You only run into this inverted subtyping relation when you, for example, pass a function as a parameter to another function, as you do with mixins.

    unknown is one of the widest types, and so will be the most restrictive when used as a function parameter type. For the function type to be as wide as possible, the parameter type must be as narrow as possible. any works because, while it's the widest type, it's also one of the narrowest. The counterintuitive conclusion is that the type of ...args should be never[].

    I ran into this problem today when I needed to describe a type that would fit the right operand of instanceof. Since the code had no business in actually calling the constructor, never[] did the trick. For mixins, however, it seems like they need to take the constructor in, as well as return an interface based off it. With my limited TS experience, I don't see any other way of achieving this than with any.

  9. tysoncung commented on Oct 7, 2025

    @tysoncung

    I'm interested in working on this issue. Could you provide more context about what you're looking for? Any additional details about requirements or constraints would be helpful.

  10. alisa-xh commented on Apr 14, 2026

    @alisa-xh

    👋 你好!

    我是专业开发者,专注于开源项目贡献。

    我可以帮你:

    • 🐛 Bug修复
    • ✨ 功能开发
    • 📝 代码优化
    • 🔧 维护工作

    经验:

    • 5年+开发经验
    • 熟悉 Python, JavaScript, TypeScript
    • 多个开源项目Contributor

    合作方式:
    请加微信详谈:qmyc-01(备注GitHub)

    期待合作!

  11. RyanCavanaugh commented on Apr 15, 2026

    @RyanCavanaugh
    Member

    snarbles2 I'm tempted to set up a script to auto-ban anyone who gets a 🚀 reaction from you 😅

  12. LeonxLJX commented on Sep 1, 2026

    @LeonxLJX

    I'd like to take this one — I'll follow up with a PR. (claiming via Stella (@LeonxLJX))

  13. LeonxLJX commented on Sep 1, 2026

    @LeonxLJX

    Hi! I'd like to fix the overly-picky constructor type check. Plan: reproduce, adjust the check, add a test. May I be assigned?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScriptDomain: classesBehavior of various `class` constructs, e.g. mixins or base classesGood First IssueWell scoped, documented and has the green lightHelp WantedYou can do this

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions