You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Regression causes Function top-type to be callable with no argumentsΒ #48840
declareletfoo: (...args: never)=>void;foo=(x: string)=>{};foo();// no error
π Actual behavior
foo() was permitted.
π Expected behavior
foo() should not be permitted because it is unsafe. (...args: never) => unknown behaves as the top-type for all functions; all function types are assignable to it. So calling it will lead to unsafe behavior by functions expecting 1 or more arguments.
π Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of the repro in the issue body running against the nightly TypeScript.
I think this aliasing unsoundness is actually unrelated to whether (...args: never) => void is callable. Consider (...args: never[]) => void or even (...args: string[]) => void. The assignment to foo in your example would still work, and yet these types are obviously callable with no arguments.
I've always considered this to be the distinction between (...args: never) => unknown and (...args: never[]) => unknown. The latter has to be callable with no arguments because never[] is assignable from []. Otherwise, without (...args: never) => unknown, there is no top-type for all functions.
Otherwise, without (...args: never) => unknown, there is no top-type for all functions.
Well, (...args: never[]) => unknown is still a function top type in that every other function type is assignable to it (the two formulations in question are cross-assignable). But it does feel like you ought to be able to express a function type constraint that is itself not callable because you have no idea what to pass it π€
It seems there are two different unsoundnesses being discussed here:
declareletfoo: (...args: never)=>void;foo();// should error (`[]` isn't assignable to `never`), but does not
declareletfoo: (...args: never[])=>void;foo=(x: string)=>{};// should error (`never[]` isn't assignable to `[string]`), but does not
Both of these unsoundnesses relate to a top function type being callable; however, the correct fix for the former is to make the type (...args: never) => void uncallable but still a top function type, while the correct fix for the latter is to make the type (...args: never[]) => void not a top function type but still callable.
We shouldn't even let you write declare let foo: (...args: never) => void;. It doesn't make sense.
The second assignment isn't unsound per our definition of rest args being assumed to have sufficient arity (or more accurately, it's unsound in the way that all rest args / finite parameter lists are unsound)
We shouldn't even let you write declare let foo: (...args: never) => void;. It doesn't make sense.
Are you saying that the type (...args: never) => void doesn't make sense and should be removed? It seems perfectly logical as a top function type; the type of ...args is, in a sense, the intersection of all possible argument types, which is never. You can't call a function of type (...args: never) => void, because you don't know what arguments it takes, but you know that it is something that could be theoretically called, you just don't know what it could be called with. It's like {} being the top type of property-bearing things; you can't directly access properties on it, because you don't know what properties it might have, but you know that it has properties that could theoretically be accessed.
The second assignment isn't unsound per our definition of rest args being assumed to have sufficient arity (or more accurately, it's unsound in the way that all rest args / finite parameter lists are unsound)
Ah, good point.
Reacted by Ryan Cavanaugh, Joe Calzaretta and Andrew Branch
So, just running into this again, presumably declare const f: (...args: never[]) => unknown should allow f() because [] is assignable to never[], but declare const g: (...args: never) => unknown should not allow g() because [] is not assignable to never. And therefore (...args: never) => unknown is a top type for functions but (...args: never[]) => unknown shouldn't be.
Right now both types are callable with no arguments and both of these are considered top types for functions. Which of these can we fix without breaking everyone?
Would it break the universe if we stopped (...args: never[]) => unknown from being considered a top type? No function that requires an argument should be assignable to it. (e.g., const f: (...args: never[])=>unknown = (x: string) => x.toUpperCase(); f(); compiles and explodes at runtime.) I'm guessing it's been used as a top type in various real-worldy places and these would be broken if the change were made. But I'd at least like to see an official "we know this is unsound but it's too late to touch it now" comment somewhere I can refer people to.
Joe Calzaretta (@jcalz) are you talking generally about assigning to functions with rest parameters (so stuff like this would also become illegal under your proposal)? Or are you suggesting that never[]-typed rest parameters be special-cased?
If it's the former, see discussion in #9352 and #58552 for rationale. Apparently this hole was plugged in a historical version of TypeScript (I was unable to determine which) and people complained. Also some standard APIs like String#replace basically expect this sort of thing to be allowed (I personally wouldn't mind being forced to use optional parameters with APIs like that, but then again I'm also the kind of masochist who enables --noUncheckedIndexedAccess).
Activity
typescript-bot commentedon Apr 25, 2022
π Hi, I'm the Repro bot. I can help narrow down and track compiler bugs across releases! This comment reflects the current state of the repro in the issue body running against the nightly TypeScript.
Issue body code block by webstrand
π Compiled
Historical Information
π Compiled
andrewbranch commentedon Apr 25, 2022
TypeScript Bot (@typescript-bot) bisect good v3.8.3 bad v3.9.7
andrewbranch commentedon Apr 25, 2022
I think this aliasing unsoundness is actually unrelated to whether
(...args: never) => voidis callable. Consider(...args: never[]) => voidor even(...args: string[]) => void. The assignment tofooin your example would still work, and yet these types are obviously callable with no arguments.typescript-bot commentedon Apr 25, 2022
The change between v3.8.3 and v3.9.7 occurred at 0e15b9f.
webstrand commentedon Apr 25, 2022
I've always considered this to be the distinction between
(...args: never) => unknownand(...args: never[]) => unknown. The latter has to be callable with no arguments becausenever[]is assignable from[]. Otherwise, without(...args: never) => unknown, there is no top-type for all functions.webstrand commentedon Apr 25, 2022
makes sense, fundementally, because
[string]is assignable tonever[]andrewbranch commentedon Apr 25, 2022
Well,
(...args: never[]) => unknownis still a function top type in that every other function type is assignable to it (the two formulations in question are cross-assignable). But it does feel like you ought to be able to express a function type constraint that is itself not callable because you have no idea what to pass it π€webstrand commentedon Apr 25, 2022
For real-world use, this most commonly shows up in my code like
or
andrewbranch commentedon Apr 25, 2022
Actually I found a counterexample from AnyhowStep by reading around #48840 (comment): https://www.typescriptlang.org/play?#code/PQKhCgAIUhlBDAZgUwE6QG5oM4EsD2AdpPopAAYBiAroQMYAuBh5kA7gBa50eTYf5qAGwAmkQvgaQARskh14QochEA6KDACiWVAE9IiWo2Z8BwsbMjxseAOaF405ZAb4XXbABoZ1KQw+mgqLikjJy1nYOTnKukMg6+ob0TETq0MDgyAAeAA74qH66OXIAgoS6NMkmALyQABSqjfCottgAXOLxaADaALoAlJDVAHyY+LgiANzg4EnGRAb4+AA8ACpxWQzIhCLYVuV9w3WDAN5QkBd0RNhSiIQAjB0NjajINx2rgyNjE0P1jaoAPpfUYnAC+0wul2ut0IACYOmUKkYUsRand7pBgMA4qhUPlwGCgA
jack-williams commentedon Apr 26, 2022
But
[string]isn't assignable tonever[]. Seems that that there is another case of unsoundness with(...args: never[]) => unknown.I think it is just broken that
(...args: never) => unknownis callable.jcalz commentedon Jun 1, 2022
So can we confirm that this is a bug and not just "needs investigation" anymore, given the discussion in #35438 ?
tjjfvi commentedon Oct 6, 2022
It seems there are two different unsoundnesses being discussed here:
Both of these unsoundnesses relate to a top function type being callable; however, the correct fix for the former is to make the type
(...args: never) => voiduncallable but still a top function type, while the correct fix for the latter is to make the type(...args: never[]) => voidnot a top function type but still callable.RyanCavanaugh commentedon Oct 6, 2022
We shouldn't even let you write
declare let foo: (...args: never) => void;. It doesn't make sense.The second assignment isn't unsound per our definition of rest args being assumed to have sufficient arity (or more accurately, it's unsound in the way that all rest args / finite parameter lists are unsound)
tjjfvi commentedon Oct 6, 2022
Are you saying that the type
(...args: never) => voiddoesn't make sense and should be removed? It seems perfectly logical as a top function type; the type of...argsis, in a sense, the intersection of all possible argument types, which isnever. You can't call a function of type(...args: never) => void, because you don't know what arguments it takes, but you know that it is something that could be theoretically called, you just don't know what it could be called with. It's like{}being the top type of property-bearing things; you can't directly access properties on it, because you don't know what properties it might have, but you know that it has properties that could theoretically be accessed.Ah, good point.
RyanCavanaugh commentedon Oct 7, 2022
Upon further reflection, I think you're right about
(args: never)jcalz commentedon Dec 30, 2022
So, just running into this again, presumably
declare const f: (...args: never[]) => unknownshould allowf()because[]is assignable tonever[], butdeclare const g: (...args: never) => unknownshould not allowg()because[]is not assignable tonever. And therefore(...args: never) => unknownis a top type for functions but(...args: never[]) => unknownshouldn't be.Right now both types are callable with no arguments and both of these are considered top types for functions. Which of these can we fix without breaking everyone?
webstrand commentedon Jan 7, 2023
I've been using and teaching
(...args: never) => unknownas the supertype of all callables.jcalz commentedon Mar 6, 2023
Would it break the universe if we stopped
(...args: never[]) => unknownfrom being considered a top type? No function that requires an argument should be assignable to it. (e.g.,const f: (...args: never[])=>unknown = (x: string) => x.toUpperCase(); f();compiles and explodes at runtime.) I'm guessing it's been used as a top type in various real-worldy places and these would be broken if the change were made. But I'd at least like to see an official "we know this is unsound but it's too late to touch it now" comment somewhere I can refer people to.mkantor commentedon Jul 5, 2025
Joe Calzaretta (@jcalz) are you talking generally about assigning to functions with rest parameters (so stuff like this would also become illegal under your proposal)? Or are you suggesting that
never[]-typed rest parameters be special-cased?If it's the former, see discussion in #9352 and #58552 for rationale. Apparently this hole was plugged in a historical version of TypeScript (I was unable to determine which) and people complained. Also some standard APIs like
String#replacebasically expect this sort of thing to be allowed (I personally wouldn't mind being forced to use optional parameters with APIs like that, but then again I'm also the kind of masochist who enables--noUncheckedIndexedAccess).