Skip to content

export type * from 'somewhere' #48508

Closed
Closed

Description

@trusktr

Suggestion

🔍 Search Terms

typescript export type star

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Just like export * from 'foo', we should be able to export type * from 'foo' to re-export all types (and only types).

📃 Motivating Example

This is useful in cases where we re-export certain things for runtime, but still wish to re-export all types:

export {one, two, three} from 'foo' // re-export certain runtime things.
export type * from 'foo' // re-export all types.

💻 Use Cases

☝️

Activity

  1. whzx5byb commented on Apr 1, 2022

    @whzx5byb

    Related: #36966

  2. RyanCavanaugh commented on Apr 4, 2022

    @RyanCavanaugh
    Member
  3. andrewbranch commented on Apr 4, 2022

    @andrewbranch
    Member

    Hm, yeah, kind of. If this syntax were allowed, it wouldn’t do what the suggestion says (“re-export all types (and only types)”) because that’s not how type-only imports/exports work. Consider named exports as an analogy:

    export type { someValue } from "foo"

    While not particularly useful, this is allowed. When imported from here, someValue is only allowed to be used in a non-emitting position because it’s type-only, but it’s also only allowed to be used in a value position because it’s only a value. I.e., the only valid usage is in a TypeQuery: let x: typeof someValue.

    The way to think about type-only imports and exports is that they have identical resolution semantics to their non-type-only counterparts, but impose restrictions on any references to them. So export type * from "foo" would export everything that export * from "foo" exports, values included, but those values would only be referenceable in non-emitting positions. It would be overridden, in a sense, by other non-type-only exports, like in the OP’s Motivating Example. If I’m importing from the file in that example, it might look like this:

    import { one, two, three, four, TypeOne, TypeTwo, TypeThree } from "./foo-reexporter"
    
    one;   // ok
    two;   // ok
    three; // ok
    four;  // Error: 'four' cannot be used as a value because it was exported using 'export type'.
    
    type T1 = TypeOne; // ok
    // ...

    It sounds like Joe Pea (@trusktr) would have expected the import of four to fail, whereas the reality is that the usage of four would fail, unless used in a typeof query. Not the end of the world, but a little confusing.

  4. RyanCavanaugh commented on Apr 4, 2022

    @RyanCavanaugh
    Member

    Joe Pea (@trusktr) given the above, would that still fit your use case?

  5. bbrk24 commented on Apr 14, 2022

    @bbrk24

    I ran into this just now, in a slightly different way:

    foo.d.ts:

    export type T1 = // ...

    bar.ts:

    import { T1 } from './foo';
    
    export function f(arg: T1): boolean {
        // ...
    }

    index.ts:

    export * from './foo';
    export * from './bar';

    This crashes at runtime for a slightly different reason -- there is no foo.js to re-export, so the export * fails entirely. (The import in bar.ts is correctly removed.)

  6. andrewbranch commented on Apr 14, 2022

    @andrewbranch
    Member

    While it’s true that this feature request would get around that, it’s not a bug—a module foo.d.ts implies the existence of a module foo.js. If you’re hand-authoring a file that’s just types, you should generally name it .ts.

  7. bbrk24 commented on Apr 14, 2022

    @bbrk24

    a module foo.d.ts implies the existence of a module foo.js. If you’re hand-authoring a file that’s just types, you should generally name it .ts.

    Andrew Branch (@andrewbranch) Is that documented anywhere? I haven't heard of this before, and I've seen standalone .d.ts files before (if nowhere else, at least in type-fest).

  8. andrewbranch commented on Apr 14, 2022

    @andrewbranch
    Member

    I don’t know if that’s explicitly stated anywhere, it’s just kind of a natural consequence of what declaration files are. When you compile a .ts file, you get a .js and a .d.ts file out. The .d.ts represents everything TypeScript needs to know about what’s in the JS file, while the JS file is (obviously) what the runtime needs to execute. They’re two sides of the same coin. This really has nothing to do with export *, as you can observe the same thing with imports:

    import * as types from "type-fest"
    console.log(types)

    There is no error here, and yet this will crash at runtime, because all the module .d.ts files in type-fest imply that there are modules, exporting nothing, all over that package, where there are none. (Also, any usage of type-fest with --importsNotUsedAsValues=preserve, imported without import type, will cause a runtime crash that we can’t predict.) While we have type-only imports and exports, we don’t currently have a concept of a type-only external module. Having that is a coherent suggestion, but not related to this issue. (The main reason I always suggest people use .ts for their type-only modules in their own projects is because .d.ts files will not get copied to outDir, so it just saves some pain. Type libraries on npm don’t have great options—they have to choose between defining everything as globals, publishing just .d.ts files that imply the existence of missing .js files, or publishing otherwise useless empty JS files alongside those .d.ts files.)

  9. frank-dspeed commented on Apr 15, 2022

    @frank-dspeed

    Andrew Branch (@andrewbranch) type only external module would address a lot of my use cases where i abuse rollup to bundle generated typedefinitions for the correct bundels. i could avoid that and create a typescript file .ts that generates only the needed declarations directly matching the output

    as i always generate the output from the list of entrypoints i get bundels for each entrypoint so every entrypoint needs exact the same type only .d.ts files

    the entrypoints are most of the time and that needs to be the only supported case files that contain only import statements and export statements

    when typescript would have a sense of a type only module it could generate the correct .d.ts files for that bundels via a .ts or .d.ts file that does type only reExports

    that also would save a lot of tsconfig hustle.

    by the way i invented type only files via checkJs allowJs containing jsdoc annotated import('./myType').TypeName

    and building that it is my hacky workaround for that .js files can easy get excluded in typescript compile steps as also they can get easy compiled with emitDeclaration only which is in fact a external typeOnly module. (declaration)

    the typescript project can this way stay untouched and behave like normal while i have outside of it a referencing project that only builds the .js entrypoints typedeclarations using the types from the typescript project. sure i could also use .ts files but that always produces wirred quirks and needs more config. Also the .js method worked before there was a type only import.

    and makes clear that the file is only about types. as it contains no real code that would get bundled or compiled by typescript.

  10. trusktr commented on May 29, 2022

    @trusktr
    ContributorAuthor

    Joe Pea (@trusktr) given the above, would that still fit your use case?

    In my case yeah. I didn't think that far into it: I had a bunch of interfaces, without any values in the module, and I simply wanted to re-export them all.

  11. Ulrikop commented on Jun 20, 2022

    @Ulrikop

    I would also welcome an export type * since the compiler no longer automatically removes export * statements from a .d.ts file (since version 3.9 I believe), which made barrel files really difficult, including type re-exports.

    In my opinion, .d.ts files have a right to exist even without a corresponding .js file. for example for the definition of a protocol.
    We are defining the events, socket messages, rest data, .... in own .d.ts files which can be shared between different services.

    the data are received and stored in an object. the corresponding class file could be different in the different service. most of the times the class differs a little bit, because it provides only required values and casts date stings to date objects.

    that is only one example. I believe there are more.

    I think TS should be flexible enough to deal with different teams approaches.
    I also think that the decision back then not to let the compiler automatically remove an export * from a .d.ts was wrong, but an export type * might work around the problem (at least for my team). And since there is already an export type {} this solution makes sense to me.

  12. nicolo-ribaudo commented on Jun 26, 2022

    @nicolo-ribaudo
    Contributor

    Duplicate of #37238, I'm cross-posting my use-case:

    We have a file that only contains type annotations (https://github.com/babel/babel/blob/main/packages/babel-types/src/ast-types/generated/index.ts), and we currently re-export it internally using export * from "..." (https://github.com/babel/babel/blob/d4d08d906a8c5c53cdf5fb0024def91fbfa51698/packages/babel-types/src/index.ts#L101).

    When publishing, we strip away TS types and compile to CJS. The types-only files becomes an empty CJS module, and our entry point ends up having some unnecessary code for export * from "...".

    Apparently, that empty CJS file causes problems with some tools (babel/babel#14634, https://stackoverflow.com/questions/71045195/cannot-build-run-devserver-cause-of-babel-loader, https://openclassrooms.com/forum/sujet/babel-loader-not-working, kriszyp/lmdb-js#51). It's a bug with those tools, but it would be nice if we could entirely avoid it by marking that re-export as type-only since it's a file that is not needed at runtime.

  13. andrewbranch commented on Jun 27, 2022

    @andrewbranch
    Member

    I’m going to go ahead and close this as a duplicate of #37238 and redirect any further conversation there as that one has a lot more eyes on it. Thanks Nicolò Ribaudo (@nicolo-ribaudo).

  14. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions