Open
Description
π Search Terms
recursive any noImplicitAny d.ts
π Version & Regression Information
- This is the behavior in every version I tried (3.3 through 5.2), and I reviewed the FAQ for entries about anything related to this.
β― Playground Link
π» Code
const a = () => a;
π Actual behavior
Any is implicitly introduced in the d.ts:
declare const a: () => any;
Note that the local type checking (not using the d.ts) correctly handles this recursive type without introducing "any", and builds fine with noImplicitAny enabled.
π Expected behavior
Either generate something without implicitly introducing any
, or generate an error if noImplicitAny
is enabled.
In this case, the below code generation for the d.ts would work:
declare const a: () => typeof a;
Additional information about the issue
No response
Metadata
Metadata
Assignees
Labels
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
fatcerberus commentedon Sep 22, 2023
Iβm more surprised this doesnβt have a circularity error, since
a
genuinely refers to itself in its own initializer and the fully expanded type ofa
is therefore() => () => () => ...
ad infinitum (I donβt think TS generally inferstypeof
types)CraigMacomber commentedon Sep 22, 2023
I just found a workaround which actually both avoids the need for inferring a
typeof
type in the d.ts, which for some reason also generates atypeof
type in the d.ts. Well, two redundant workarounds for the price of one is still a workaround :)The d.ts does not produce
any
, instead it usestypeof
and adds aMySchema_base
constant that does not exist in the JS.playground link
fatcerberus commentedon Sep 23, 2023
Looking at this again, I think there might be a genuine bug here, just not the one presupposed in the OP; this isn't actually an implicit
any
and in fact does something I didn't even know was possible:It seems like TypeScript knows the type is infinitely expanding, and that's totally fine. The problem only arises when it has to generate a declaration for it, at which point it apparently falls over and defaults to
any
.a
is not treated asany
locally; you can't assign it to anumber
orstring
for example, but can write any number of pairs of parentheses aftera()
and it still typechecks. Very odd.CraigMacomber commentedon Sep 26, 2023
@fatcerberus That is the bug I was attempting to report. Everything is correct locally, but the generated d.ts contains "any" which was not explicitly in the source anywhere. I'll update it to clarify that it works locally in the description.
fatcerberus commentedon Sep 26, 2023
@CraigMacomber I thought so, but I wanted to be sure after you said this:
which isnβt really relevant IMO since thereβs no implicit
any
in the sense thatnoImplicitAny
is meant to guard against (i.e. type inference failure introducing an implicit any); itβs seemingly just the declaration emitter falling over on something whose type was already correctly inferred.CraigMacomber commentedon Sep 26, 2023
Its implicitly introducing any in the API for my package via the d.ts file.
TypeScript has a known design decision/limitation that the API of .dts files doesn't match the local type checking exactly, so the fact that they are different is not always a bug.
Its also a known design limitation of TypeScript that sometimes recursive types don't work arbitrarily, and you get
any
instead. This is also not a bug.Thus I'm highlighting that the produced type is violating the noImplicitAny rule, which I think makes it a bug.
RyanCavanaugh commentedon Oct 3, 2023
It seems very reasonable to emit a
noImplicitAny
error when this happens specifically during declaration emit.any
type for self-referring class expressions #56479andrewbranch commentedon Nov 21, 2023
#56479 should be included as a test case for this.
jakebailey commentedon Nov 21, 2023
Further, it'd be awesome to emit some sort of error any time dts emit produces an
any
out of nowhere, but when we were talking in theisolatedDeclaration
meeting, such a thing seemed quite challenging given this is all pretty deep intypeToTypeString
. But, maybe some sort of flag or something would be sufficient..._base
class inserted by compiler is not exported. microsoft/rushstack#4429tree2: Class based schema (#18350)