-
Notifications
You must be signed in to change notification settings - Fork 85
Description
Describe the feature
When "resolving" imports to format them, they are first deduped with the dedupeImports function. Currently, the main criteria to decide whether to dedupe are the name and from properties of an Import. However, with TypeScript, it is possible and valid to have two exports in the same module with the same identifier if one is for the value and other for the type, which is what enables using class identifiers as both values (new SomeClass()) and types ((p: SomeClass) => void). Therefore, the following imports should not be deduped to account for this use case:
[{
name: identifier,
from: specifier
},{
name: identifier,
from: specifier,
type: true
}]Example
Here is a demo using generateTypeDeclarations with an imports array like the one shown above. To test run node index.js. Due to deduplication, the output depends on the order of the imports. Possible outputs are:
export {}
declare global {
const Test: typeof import('./module').Test
}
export {}
declare global {
}
// for type re-export
declare global {
// @ts-ignore
export type { Test } from './module'
import('./module')
}
If these are used to create d.ts files, the Test identifier will only be usable as either a value (first case) or a type (second case), but not both.
Workaround
Imports support a declarationType: 'class' property, which could be used to obtain the desired output. However, this property does not seem designed to solve this particular issue; using declarationType: 'class' simply exempts an import from being deduplicated, so both import entries are still required. Two additional issues with this approach are:
- (truly) duplicate imports with
declarationType: 'class'are not deduplicated. - it does not work well with inline presets, where a list (array) of export names is provided.
More motivation
Exported values and types with the same identifiers are not exclusive to the export class syntax. For instance, in TypeScript a class' type can be customized with the following pattern:
class TestImpl {}
interface TestConstructor {}
interface TestInstance {}
export type Test = TestInstance
export const Test: TestConstructor = TestImpl as anyTherefore, I think the most reliable solution would be for the dedupeImports function to support not deduping imports that are "complementary", i.e., same module and identifier (from and name), but different scope (type).
I am planning to submit a PR refactoring dedupeImports to address this.
Additional information
- Would you be willing to help implement this feature?