Repository navigation
TS is overly picky when declaring a class constructor type #29707
Description
Activity
dragomirtitian commented
on Feb 3, 2019 ContributorMore actionsIn 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,
unknownalong came a long time after this PR, maybe the rules can be changed.Reacted by Boris Cherny and Sean AlunniRyanCavanaugh commented
on Feb 4, 2019 MemberMore actionsAgree,
unknown[]should be an acceptable substitute. Accepting PRs for an easy fix.Reacted by Titian Cernicova-Dragomir and Sayan Bhowmik- addedHelp WantedYou can do thisYou can do thisGood First IssueWell scoped, documented and has the green lightWell scoped, documented and has the green light
on Feb 4, 2019 dragomirtitian commented
on Feb 4, 2019 ContributorMore actionsRyan 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 candidateconformance/classes/mixinClassesAnnotated.ts)RyanCavanaugh commented
on Feb 4, 2019 MemberMore actionsTitian Cernicova-Dragomir (@dragomirtitian) no preference; whichever's easiest for you
Reacted by Titian Cernicova-Dragomirdragomirtitian commented
on Feb 4, 2019 ContributorMore actionsRyan 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 asstrictFunctionTypesare not turned on. When that flag is on this code becomes an errorclass 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
unknownarguments.RyanCavanaugh commented
on Feb 4, 2019 MemberMore actionsThat's why it's a constraint (not a concrete type) though, right?
dragomirtitian commented
on Feb 5, 2019 ContributorMore actionsRyan 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 understrictFunctionTypesfunction 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
numberbut it's called insidetestwith astring. I think the current behavior is better, don't let a function with anumberparameter be assigned to a function with anunknownparameter, the implementing function can't handleunknown.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 theno-anytslint rule not sure whyunknownis better in this instance. At leastanywould function as a '💀💀 careful unsafe 💀💀' warning, at least that's the way I seeany:)Reacted by Alex StrizhakThe 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.
unknownis 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.anyworks because, while it's the widest type, it's also one of the narrowest. The counterintuitive conclusion is that the type of...argsshould benever[].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 withany.Reacted by Vivek :), Jongsun and snarbles2I'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.
- addedDomain: classesBehavior of various `class` constructs, e.g. mixins or base classesBehavior of various `class` constructs, e.g. mixins or base classes
on Oct 16, 2025 👋 你好!
我是专业开发者,专注于开源项目贡献。
我可以帮你:
- 🐛 Bug修复
- ✨ 功能开发
- 📝 代码优化
- 🔧 维护工作
经验:
- 5年+开发经验
- 熟悉 Python, JavaScript, TypeScript
- 多个开源项目Contributor
合作方式:
请加微信详谈:qmyc-01(备注GitHub)期待合作!
Reacted by snarbles2RyanCavanaugh commented
on Apr 15, 2026 MemberMore actionssnarbles2 I'm tempted to set up a script to auto-ban anyone who gets a 🚀 reaction from you 😅
Reacted by snarbles2Reacted by snarbles2 and rbI'd like to take this one — I'll follow up with a PR. (claiming via Stella (@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?
TypeScript Version: 3.4.0-dev.20190202
Search Terms: class, extend, constructor, any[]
Code
Expected behavior:
I should be able to replace
...args: any[]with...args: unknown[], or any other signature.Actual behavior:
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