import { A } from "https://example.com/type.d.ts";
type B = A | A
If you hover on B in both TS Playground and VS Code, it will display it as A in the case type B = A and any in the case type B = A | A. Regardless of whether A is known or not, I expect they should be consistent. Why is that not the case?
According to microsoft/TypeScript#54630, specifically this comment by the TS team dev lead:
When you import something that can't be found, you get a compiler error:
and any references to it are going to be shown as whatever TypeScript wants to show it as. It will often tend to show up as the
anytype, becauseThat explains why you see
anysometimes.As for why it shows
Ainstead ofanywhen you change fromto
there's no documented and stable rule by which such types are displayed. Assuming that
Ais sometimes displayed asany, then it's really the compiler's prerogative to decide which one of those it wants to display. Even ifAweren't an error, you'd still have such a dichotomy:There you have type
D, which is clearly an alias for{a: string}. Some uses ofDget reduced to{a: string}, while others stay asD.These differences are only how the type gets displayed to the user, and shouldn't affect the actual identities of the types. There are no supported official mechanism by which a developer can just tell the compiler which form she would prefer to see when displaying a type. There are various open feature requests like microsoft/TypeScript#28508, microsoft/TypeScript#31940, and microsoft/TypeScript#45954, which could possibly change that if they were implemented, but for now it's not part of the language. The compiler uses heuristics to determine which is the type to display; these heuristics perform well enough in a wide range of use cases, but it is necessarily the case that they cannot meet everyone's needs simultaneously. For every person who wants
Dto be displayed as{a: string}, there is an equal and opposite person who wants it do be displayed asD, and they both have very important reasons for it to be that way, and both think their way would make things "consistent". More developer control would be nice, but without that, you just get what you get.So: I can't easily say for certain why you see
anyforA | AbutAforA, without stepping through the TypeScript compiler code and maybe finding pull requests that document the parts responsible. But even if I did that, it wouldn't be particularly enlightening, and it could easily change in a future version of TypeScript. As long as two types are identical, in some sense it's completely up to the compiler to decide which one to show and when.Playground link to code