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
3.8 RC Regression? "Individual declarations in merged declaration" #36684
Expected behavior:
Expected that this compiles and users can refer to Greeting as both the type {greet: string} and the namespace of functions/data (just hello in this case). In versions 3.7.5 and 3.8.0-beta, this was OK.
Actual behavior:
3.8.1-rc and current nightly produce these errors:
use.ts:1:13 - error TS2395: Individual declarations in merged declaration 'Greeting' must be all exported or all local.
1 import * as Greeting from './ns'
~~~~~~~~
use.ts:3:13 - error TS2395: Individual declarations in merged declaration 'Greeting' must be all exported or all local.
3 export type Greeting = Greeting.Type
~~~~~~~~
I guess we accidentally fixed that case, which we had previously accidentally left unfixed, sometime after the beta? Possibly with #36237? Sorry Daniel Rosenwasser (@DanielRosenwasser), I should have recognized #36237 as a breaking change 😕
Garrett Mitchell (@garrettm) the issue isn’t with what will merge as exports, it’s that imports are never allowed to merge with local declarations. You could rewrite your original example as
It seems like the syntax is fine but I can't actually import the value of ns from use.ts.
// example-1.tsimport{Greeting}from"./use.ts";letgreeting: Greeting={greet: "hi"};Greeting.hello(greeting);// 'Greeting' only refers to a type, but is being used as a value here.
Oddly enough, it seems to work when I import the entire module with a wildcard.
But I've just discovered that it breaks tree shaking for Rollup (and presumably other bundlers). Anything that imports use.ts will end up with a full copy of the ns.ts module, irrespective of which exports they're actually using.
The important difference seems to be that tree shaking only works when the values are re-exported directly.
// works with tree shaking, not with typescriptexport{nsasGreeting};// works with typescript, not with tree shakingexportconstGreeting=ns.Greeting;
Hmm curious, I'm not having any luck with the latest non-beta TSC with Andrew Branch (@andrewbranch)'s workaround anymore either. Maybe I had a slightly different setup, or maybe this changed in a 3.8.x?
One downside of Dan Prince (@danprince)'s workaround is that you can't export types from the namespace, you get this error when you try to use it: 'Editor' only refers to a type, but is being used as a namespace here.
Dan Prince (@danprince) sorry I missed your comment earlier; that looks like it’s probably a bug. The Greeting symbol in export { ns as Greeting } is clearly not merging with the Greeting symbol in export type Greeting = ns.Type. I’m now second-guessing myself as to whether they’re supposed to merge, but the correct behavior is either
Give a duplicate identifier error at both exports, or
be able to reference both the type and the namespace meanings when you import it.
You’re seeing neither of those, which I’m pretty confident is wrong.
Hey Andrew Branch (@andrewbranch)! Thanks for clarifying here. Is this behaviour going to be considered for a post 3.9 release?
Spending a lot of time writing Rectangle.Rectangle and Vector.Vector at the moment and it would be great to chop them down, or at least to know it's a wontfix.
It’s definitely not going to be included in a 3.9.x release, as the window for anything besides critical fixes closed a while ago. But I’m pretty sure the “bug” here is that we don’t give an error sooner:
exporttypeGreeting={};export{nsasGreeting};
These two exports shouldn’t be allowed to coexist. In a way, it looks like they already don’t coexist because the alias meaning is unreferenceable from an import, but we should be giving you an error here on the exports. So I don’t think we’re really looking at any work that will give you the results you’re looking for. If it’s important to you, you should open a new issue, but I’d suggest including some real-world code that demonstrates why this is important. From my perspective here, it looks a little bit like an aesthetic problem that a slight reorganization of file structure or a slight reimagining of names would solve, and it’s hard to understand why it matters with examples like Greeting.
Andrew Branch (@andrewbranch) here's a slightly more real code example that's basically a real world example with details stripped out and trimmed down a lot for length.
Users of this file want to import * as Animal from './animal' which works for everything except the exported type Animal, which has to be referred to as Animal.Animal. I'm not aware of a way to keep the nice namespacing of import * as and also refer to the Animal type itself easily.
One workaround is:
import * as A from './animal' import {Animal} from './animal'
But this is inconvenient + less clear
Spending a lot of time writing Rectangle.Rectangle and Vector.Vector at the moment and it would be great to chop them down, or at least to know it's a wontfix.
Every single one of these files is imported like import * as Blah from "./Blah";...
...which means I write Asset.Asset, Color.Color, Company.Company, etc. a lot!
Activity
DanielRosenwasser commentedon Feb 10, 2020
Andrew Branch (@andrewbranch) I thought this was a break in 3.7
DanielRosenwasser commentedon Feb 10, 2020
We discussed it in #33422 and I'm pretty sure the relevant issue was #31231.
andrewbranch commentedon Feb 10, 2020
Daniel Rosenwasser (@DanielRosenwasser) it looks like #31231 simply failed to handle the combination of namespace import + type alias... 😱
andrewbranch commentedon Feb 10, 2020
I guess we accidentally fixed that case, which we had previously accidentally left unfixed, sometime after the beta? Possibly with #36237? Sorry Daniel Rosenwasser (@DanielRosenwasser), I should have recognized #36237 as a breaking change 😕
garrettm commentedon Feb 13, 2020
Andrew Branch (@andrewbranch) Daniel Rosenwasser (@DanielRosenwasser) this is working as intended? So, I'll need to change my code to upgrade to 3.8+?
DanielRosenwasser commentedon Feb 13, 2020
Yes, I believe so. It's something I wish we could've communicated sooner, but the code never should have compiled.
garrettm commentedon Feb 13, 2020
Okay, thanks. For the record, this pattern can be really useful in terms of exposing a type + a set of functions on that type.
You can do this with an explicit namespace in a file, but I thought that was not encouraged anymore. Like this:
These seem isomorphic and it seems weird to allow one and not the other.
andrewbranch commentedon Feb 14, 2020
Garrett Mitchell (@garrettm) the issue isn’t with what will merge as exports, it’s that imports are never allowed to merge with local declarations. You could rewrite your original example as
and that would be fine.
garrettm commentedon Feb 14, 2020
Andrew Branch (@andrewbranch) thanks! That worked.
typescript-bot commentedon Feb 16, 2020
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
danprince commentedon Apr 7, 2020
Garrett Mitchell (@garrettm) Andrew Branch (@andrewbranch) Do you have an example of that rewritten form working in the wild?
It seems like the syntax is fine but I can't actually import the value of
nsfromuse.ts.Oddly enough, it seems to work when I import the entire module with a wildcard.
I had been using the following workaround:
But I've just discovered that it breaks tree shaking for Rollup (and presumably other bundlers). Anything that imports
use.tswill end up with a full copy of thens.tsmodule, irrespective of which exports they're actually using.The important difference seems to be that tree shaking only works when the values are re-exported directly.
This is possibly a separate issue to the original report, but I'm not sure. Here's a repro for anyone who can dig further than I can https://github.com/danprince/ts-export-types-bug
garrettm commentedon Apr 23, 2020
Hmm curious, I'm not having any luck with the latest non-beta TSC with Andrew Branch (@andrewbranch)'s workaround anymore either. Maybe I had a slightly different setup, or maybe this changed in a 3.8.x?
Your workaround does work for me Dan Prince (@danprince)
It would be nice if this was more ergonomic in general in typescript
garrettm commentedon Apr 23, 2020
One downside of Dan Prince (@danprince)'s workaround is that you can't export types from the namespace, you get this error when you try to use it:
'Editor' only refers to a type, but is being used as a namespace here.andrewbranch commentedon Apr 23, 2020
Dan Prince (@danprince) sorry I missed your comment earlier; that looks like it’s probably a bug. The
Greetingsymbol inexport { ns as Greeting }is clearly not merging with theGreetingsymbol inexport type Greeting = ns.Type. I’m now second-guessing myself as to whether they’re supposed to merge, but the correct behavior is eitherYou’re seeing neither of those, which I’m pretty confident is wrong.
andrewbranch commentedon Apr 23, 2020
It does seem like this behavior goes all the way back to TypeScript 1.x though, so I’m not sure it’s a candidate for a last-minute 3.9 fix 😄
garrettm commentedon Apr 23, 2020
Something that makes this type of thing easy to do would be super appreciated, from this user's perspective :)
I haven't been able to find a good workaround for this at the moment
danprince commentedon May 11, 2020
Hey Andrew Branch (@andrewbranch)! Thanks for clarifying here. Is this behaviour going to be considered for a post 3.9 release?
Spending a lot of time writing
Rectangle.RectangleandVector.Vectorat the moment and it would be great to chop them down, or at least to know it's a wontfix.andrewbranch commentedon May 12, 2020
It’s definitely not going to be included in a 3.9.x release, as the window for anything besides critical fixes closed a while ago. But I’m pretty sure the “bug” here is that we don’t give an error sooner:
These two exports shouldn’t be allowed to coexist. In a way, it looks like they already don’t coexist because the alias meaning is unreferenceable from an import, but we should be giving you an error here on the exports. So I don’t think we’re really looking at any work that will give you the results you’re looking for. If it’s important to you, you should open a new issue, but I’d suggest including some real-world code that demonstrates why this is important. From my perspective here, it looks a little bit like an aesthetic problem that a slight reorganization of file structure or a slight reimagining of names would solve, and it’s hard to understand why it matters with examples like
Greeting.garrettm commentedon May 12, 2020
Andrew Branch (@andrewbranch) here's a slightly more real code example that's basically a real world example with details stripped out and trimmed down a lot for length.
Users of this file want to
import * as Animal from './animal'which works for everything except the exported type Animal, which has to be referred to asAnimal.Animal. I'm not aware of a way to keep the nice namespacing ofimport * asand also refer to the Animal type itself easily.One workaround is:
import * as A from './animal'import {Animal} from './animal'But this is inconvenient + less clear
cruhl commentedon Aug 17, 2020
Garrett Mitchell (@garrettm) This issue might help: #39865
cruhl commentedon Aug 17, 2020
Andrew Branch (@andrewbranch) I can also confirm my codebase is littered with
NamespaceAndType.NamespaceAndTypelike Dan Prince (@danprince) mentioned...Every single one of these files is imported like
import * as Blah from "./Blah";......which means I write
Asset.Asset,Color.Color,Company.Company, etc. a lot!