Background
I recently helped migrate Pinia to tsdown, vuejs/pinia#3092. One problem we encountered was with type augmentations.
Previously Pinia was using rollup, rollup-plugin-typescript2 and @microsoft/api-extractor to bundle the types. This yielded types of this form:
export declare type StoreActions = /* type details not important */;
After switching to tsdown this same type is bundled slightly differently:
type StoreActions = /* type details not important */;
export { type StoreActions };
These might appear equivalent, but they aren't. They behave differently if the type is used with a type augmentation:
// Type augmentation in app code
declare module 'pinia' {
// StoreActions is available here
// without needing to import it
}
With the original inline export, StoreActions could be accessed inside the augmentation without importing it. With the deferred export used by tsdown this doesn't work, it needs to be explicitly imported.
Is this a TypeScript bug?
I thought this might be a bug in TypeScript, so I opened this issue:
I don't understand the explanation, but apparently this is by design.
How do other tools handle it?
I did some investigation into other build tools and I wrote it up here:
I won't repeat everything I wrote there, but in short, tsdown seems to be consistent with tsup, but differs from tools like @microsoft/api-extractor and dts-bundle-generator.
I don't think any of those approaches would be considered 'right' or 'wrong', they're just different. But it is potentially a barrier to migrating to rolldown/tsdown if the types aren't backwards compatible with those generated by other build tools.
Vue core includes a 'fix' for this
Vue core recently migrated from tsup to tsdown (on the minor branch), but historically it used other build tools and so it uses a custom plugin to rewrite the types and keep them backwards compatible. You can see that here (this is for rollup, but it's essentially the same for rolldown):
This rewrites the type exports to make them all inline, rather than batching them at the end.
What next?
Maybe there could be a config option or an official plugin similar to the one used by Vue core?
I haven't been able to find any discussion/explanation of this topic, not just for rolldown but more generally. Even if nothing needs to be done I thought it would be useful to open this issue so we have somewhere to discuss and document the rationale behind any decisions that are made.
CC @posva @edison1105
Background
I recently helped migrate Pinia to
tsdown, vuejs/pinia#3092. One problem we encountered was with type augmentations.Previously Pinia was using
rollup,rollup-plugin-typescript2and@microsoft/api-extractorto bundle the types. This yielded types of this form:After switching to
tsdownthis same type is bundled slightly differently:These might appear equivalent, but they aren't. They behave differently if the type is used with a type augmentation:
With the original inline
export,StoreActionscould be accessed inside the augmentation without importing it. With the deferredexportused bytsdownthis doesn't work, it needs to be explicitly imported.Is this a TypeScript bug?
I thought this might be a bug in TypeScript, so I opened this issue:
export typevsexport { type }with augmentations microsoft/TypeScript#63061I don't understand the explanation, but apparently this is by design.
How do other tools handle it?
I did some investigation into other build tools and I wrote it up here:
I won't repeat everything I wrote there, but in short,
tsdownseems to be consistent withtsup, but differs from tools like@microsoft/api-extractoranddts-bundle-generator.I don't think any of those approaches would be considered 'right' or 'wrong', they're just different. But it is potentially a barrier to migrating to
rolldown/tsdownif the types aren't backwards compatible with those generated by other build tools.Vue core includes a 'fix' for this
Vue core recently migrated from
tsuptotsdown(on theminorbranch), but historically it used other build tools and so it uses a custom plugin to rewrite the types and keep them backwards compatible. You can see that here (this is forrollup, but it's essentially the same forrolldown):This rewrites the type exports to make them all inline, rather than batching them at the end.
What next?
Maybe there could be a config option or an official plugin similar to the one used by Vue core?
I haven't been able to find any discussion/explanation of this topic, not just for
rolldownbut more generally. Even if nothing needs to be done I thought it would be useful to open this issue so we have somewhere to discuss and document the rationale behind any decisions that are made.CC @posva @edison1105