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
Type annotation for all exports in the module #38511
Increasingly frameworks are using named module exports as a way of organizing their code. For instance Next.js is using named exports to specify things like exported component and functions for fetching data:
There are a few more details for use cases and an alternative syntax, but I don't really care too much about the specific syntax. I just want the feature. :) You've got a 👍 from me!
Reacted by Alexander Kachkaev, Владимир, Dan, Ryan Lahman, Abhishek Rawat, Jeremy Tenjo, Lennart Hildebrandt, Daniel Gonzalez, itsMapleLeaf, Michaël De Boey and 24 more
Adding SvelteKit to the list of frameworks that follows this pattern — this would be hugely beneficial to us as well.
An even better outcome for users would be if modules could be typed implicitly — for example if certain files (either with a particular naming convention or because the framework generates a config that lists them) were known to implement a particular interface, the export implements (or whatever) would be unnecessary (unless you needed to specify type variables). For example a typical SvelteKit endpoint looks like this:
...would only really start paying dividends once you had multiple exports, whereas an implicitly-typed module would provide significant benefits even to people who see the red squigglies but otherwise have no idea what TypeScript is:
Reacted by Michael Kreuzmayr, Panagiotis Kapros, Arad Alvand, Taichi, Bluzzi, Zachiah Sawyer, Hyunbin Seo, Jacob, Andrii Oriekhov, Joshua J. and 3 more
I'd also love implicit types based on a convention and the tsconfig-based configuration of that convention (😆). That would be a big benefit for Remix users as well.
I'm concerned that this would stall the feature. If there's some way to have both and start with the explicit that's the future I would prefer personally. I think explicit has a place in the TS ecosystem as well, so both make sense to me. So if we could get the explicit finished and start benefitting from that then work on the implicit that would be super :)
Reacted by Alexander Kachkaev, Michaël De Boey, Andrii Oriekhov and Joshua J.
Indeed. If feature B depends on feature A, it's better to implement feature A first rather than waiting to implement both.
Nevertheless, it's very useful in my experience to understand the rough shape of feature B (and any other dependent features you have in mind) before designing feature A, lest you end up with a design that for whatever reason makes those follow-ups hard or impossible. For that reason it's often worthwhile to include those considerations in the initial discussion.
Reacted by Kent C. Dodds, Amr Gad, Maciek Sakrejda, Michaël De Boey, Sean Vieira, Oscar Dominguez, Panagiotis Kapros, Nicky McCurdy, Katie Byers, Andrii Oriekhov and 2 more
Maybe with something less c++-y than :: but you get the idea.
I think <module-name>.<exports-types>.ts could become a fairly common convention. But equally, for all-one-type projects you could just set *.ts to be any type you like, or for Pages we could set a subdir like:
Reacted by Rich Harris, JongChan Choi (Rieul), Sean Vieira, Michael Shilman, Edmund Hung, Piotr Hebda, Shu Ding, Patrick Kunka, NeXeN, Jonathan Hefner and 2 more
Thanks Orta Therox (@orta). Looks like there's community interest and team appetite for the idea. So where do we go from here? How do we push things forward?
Reacted by Michaël De Boey, Amr Gad, Abhishek Rawat, Adrian Cooney and Joshua J.
Reacted by Michaël De Boey, Amr Gad, Abhishek Rawat and Cam Jackson
Mentioned in other threads on the topic, Storybook's Component Story Format also heavily relies on this pattern, where the default export includes metadata about a component, and each named export is a component example, or story:
In our case, users also want more type safety and better autocompletion than the general types provide, so they will parameterize them by the props of the component that is being documented. So at the top of each file they might make specific versions at the top of each file:
Nevertheless, it's very useful in my experience to understand the rough shape of feature B (and any other dependent features you have in mind) before designing feature A, lest you end up with a design that for whatever reason makes those follow-ups hard or impossible. For that reason it's often worthwhile to include those considerations in the initial discussion.
In this spirit, I just wondered about the following as a possible future extension of this proposal: A module that implements named exports may infer the types of any imports that originate from the same module that provides the named export interface.
I am not sure about the syntax and how to implement this but maybe someone can come up with something smart.
Taking Remix as an example, this would enable e.g.
// routes/posts.tsximporttype{EntryRouteModule}from'Remix'import{useLoaderData}from'Remix';typePost={title: string};exportimplementsEntryRouteModule<{posts: Post[]}>;// this is typed correctly because of the line aboveexportconstloader=async()=>{return{posts: awaitpostsQuery()}exportdefaultfunctionBlog(){// Have TypeScript figure this out automatically somehow:// useLoaderData now knows that `LoaderData === Post[]`. There is no need to `useLoaderData<Post[]>`const{ posts }=useLoaderData()return(<ul>{posts.map(post=>(<li>{post.title}</li>))}</ul>)}
This is a slightly different use case than the one outlined in the initial Next.js example, where the inference of posts === Post[] does not rely on any additional imports.
import{PageModule}from'next'typePost={title: string};exportimplementsPageModule<{posts: Post[]}>;// Here, `posts` is being inferred correctly as per proposalfunctionBlog({ posts }){return(<ul>{posts.map(post=>(<li>{post.title}</li>))}</ul>)}exportasyncfunctiongetStaticProps(){return{posts: []}}exportdefaultBlog
// @file pages/posts.tsximportReactfrom'react';// This can be more complex to cover getServerSideProps etctypeInferPropTypes<T>=Textends{getStaticProps: ()=>Promise<{props: infer U}>}
? U
: never;import*asselffrom'./posts';constPosts: React.FC<InferPropTypes<typeofself>>=({ posts })=>{return(<ul>{posts.map((post)=>(<li>{post.title}</li>))}</ul>);};exportasyncfunctiongetStaticProps(){constposts: Array<{title: string}>=[];return{props: { posts }};}exportdefaultPosts;
This is less automatic than typing all exports using something like export implements but it's possible to do today.
I would also love to see such a feature in TypeScript.
Probably every project that has file conventions would benefit from such a feature. My project typesafe-i18n could also benefit from it.
I don't think that there is a new syntax needed. What if TypeScript could use a .d.ts file to check what variables and exports a file should have?
Currently e.g. a file.d.ts would tell every other file that imports file.js what values it exports and what shape those variables and functions have. But in that file.js no type-check happens. The file.js could also export something totally different and TypeScript would not throw an error:
file.d.ts
declareexport someVariable: number
file.js
exportsomeVariable='Hello'
So if TypeScript would use the information from the d.ts file to check if the .js file was implemented correctly, we could have a solution to the typed export problem. Of course this should also work with .ts files.
With a solution like we can see in SvelteKit where types are generated and then linked with the real source files via the rootDirs compiler option, those .d.ts files could be generated without adding declaration files inside the actual src folder.
Having those checks enabled would probably break a few existing code bases if they manually create .d.ts files. Or actually they would get notified that they defined something wrong ;)
But this is a topic for a later discussion. I just want to propose an alternative to this thread.
I like that. Though in Remix at least, you can very dynamically define what files count as routes so I would like an additional capability to define a type for the exports.
From that angle, I wonder if it might be interesting to think about a design where these module-level type signatures are 'attached' to files through the tsconfig.json and equivalent language service constructs.
I see the benefit of that, especially the idea of it being enforced from the outside, because it's a framework-level expectation. My concern is that it makes the code much less discoverable. To me it's very important that a new developer can look at a piece of code, without any context, and be able to figure out what it does by following the breadcrumbs.
I prefer an API that explicitly establishes a link that says "this code that you're looking at implements this interface", and then you can click through to see what that interface is. As opposed to it being configured somewhere else, with no way of like, naturally discovering it from the code in question. It becomes something that you just have to know.
I prefer an API that explicitly establishes a link that says "this code that you're looking at implements this interface", and then you can click through to see what that interface is.
That's a great point. Perhaps the relevant comparison here is what you have defined in your tsconfig.json for the compilerOptions.lib option. This is something defined on the 'outside' that affects which ambient types will show up. For use-cases where you want to be explicit, you can opt into a triple-slash directive to make the dependency explicit.
allowing ambient module declarations to specify relative module names as an alternative to using tsconfig could be a more intuitive and/or powerful way to achieve this by way of using typescript (especially satisfies) rather than json
Reacted by Geoff Goodman, Stephen Fraser, Marius Gundersen, xpdmk, Jacob, Christian Meredith, Andrii Oriekhov, Xvezda, Abdallatif Sulaiman, georgetzlp and 6 more
Reacted by Sʜɪᴍᴜʀᴀ Yū
Reacted by Sʜɪᴍᴜʀᴀ Yū
Reacted by Matt Apperson, Olle Bröms, Marius Gundersen, Christian Meredith, georgetzlp, Sʜɪᴍᴜʀᴀ Yū and Thomas Ingram
I like export {} satisfies Type because satisfies works better than implements in this case. The only thing is the emitted export {} in runtime space that might not be desired.
I would really like this too, and would be happy with export {} satisfies Type, although some magic with tsconfig and wildcard paths would be very useful.
A workaround while we wait for this feature is to import the module into itself and then check that what is imported satisfies some type. For example:
This is quite cumbersome though, since you need to specify the name of the file in the file, and you get the error in the satisfies line, not on the export line.
Reacted by Thomas Gauges, B. Oskam, Hofer Ivan, Alex Young, Andrii Oriekhov, Thomas Ingram, Juan Campa, Thomas Seeley and Luca Cavallaro
@Rich-Harris love your idea of implicit naming based on file pattern, imo in tsconfig something like:
{
"compilerOptions": {
"exportTypings: {
"*.svelte.ts": "@sveltejs/kit::Endpoint"
}
}
}
Maybe with something less c++-y than :: but you get the idea.
I do not mind using the :: "operator" because that is common in java method references as well, and it clearly indicates you are dealing not with a call using the dot operator but a reference using the :: operator.
But as I read this I was thinking "Why is this not a feature?"
Just following up on this issue! My favorite solution is Glen Maddern (@geelen)'s proposal (quoted example above). This would be incredibly useful and I'd be happy to help contribute.
Reacted by Sébastien Lorber, Luca Cavallaro and Bluzzi
Activity
balazsorban44 commentedon May 12, 2020
You can add Remix to this category
https://blog.remix.run/p/remix-preview
It will export a load function similar to Next.js's getServerSideProps
kentcdodds commentedon Nov 29, 2021
I didn't notice this issue when I opened: #46942
There are a few more details for use cases and an alternative syntax, but I don't really care too much about the specific syntax. I just want the feature. :) You've got a 👍 from me!
Rich-Harris commentedon Nov 30, 2021
Adding SvelteKit to the list of frameworks that follows this pattern — this would be hugely beneficial to us as well.
An even better outcome for users would be if modules could be typed implicitly — for example if certain files (either with a particular naming convention or because the framework generates a config that lists them) were known to implement a particular interface, the
export implements(or whatever) would be unnecessary (unless you needed to specify type variables). For example a typical SvelteKit endpoint looks like this:Converting that to an explicitly typed module...
...would only really start paying dividends once you had multiple exports, whereas an implicitly-typed module would provide significant benefits even to people who see the red squigglies but otherwise have no idea what TypeScript is:
kentcdodds commentedon Nov 30, 2021
Hey Rich Harris (@Rich-Harris) 👋
I'd also love implicit types based on a convention and the tsconfig-based configuration of that convention (😆). That would be a big benefit for Remix users as well.
I'm concerned that this would stall the feature. If there's some way to have both and start with the explicit that's the future I would prefer personally. I think explicit has a place in the TS ecosystem as well, so both make sense to me. So if we could get the explicit finished and start benefitting from that then work on the implicit that would be super :)
Rich-Harris commentedon Dec 1, 2021
Indeed. If feature B depends on feature A, it's better to implement feature A first rather than waiting to implement both.
Nevertheless, it's very useful in my experience to understand the rough shape of feature B (and any other dependent features you have in mind) before designing feature A, lest you end up with a design that for whatever reason makes those follow-ups hard or impossible. For that reason it's often worthwhile to include those considerations in the initial discussion.
GregBrimble commentedon Dec 1, 2021
+1 from Cloudflare Pages as well!
We declare this
PagesFunctiontype in our @cloudflare/workers-types package, and encourage people to include it in theirtypesintsconfig.json:{ "compilerOptions": { "types": ["@cloudflare/workers-types"] } }With support for type-checking of modules, we could declare a
PagesFunctionsor similar:But this API would do exactly what we'd need! Thanks everyone for suggesting it :)
geelen commentedon Dec 1, 2021
Rich Harris (@Rich-Harris) love your idea of implicit naming based on file pattern, imo in tsconfig something like:
Maybe with something less c++-y than
::but you get the idea.I think
<module-name>.<exports-types>.tscould become a fairly common convention. But equally, for all-one-type projects you could just set*.tsto be any type you like, or for Pages we could set a subdir like:I'm liking this idea more and more...
orta commentedon Dec 8, 2021
Notes from the last time we talked about this: #38713 and some of the possible consensus #420 (comment)
kentcdodds commentedon Dec 10, 2021
Thanks Orta Therox (@orta). Looks like there's community interest and team appetite for the idea. So where do we go from here? How do we push things forward?
shilman commentedon Dec 16, 2021
Mentioned in other threads on the topic, Storybook's Component Story Format also heavily relies on this pattern, where the default export includes metadata about a component, and each named export is a component example, or story:
In our case, users also want more type safety and better autocompletion than the general types provide, so they will parameterize them by the props of the component that is being documented. So at the top of each file they might make specific versions at the top of each file:
Ideally a solution would be powerful enough to express this kind of specificity.
So this would fit the bill in each file:
But this proposal in its current form would not:
A combination of the former (for users who want more control) and the latter (for convenience) would be amazing. 💯
hollandThomas commentedon Jan 7, 2022
In this spirit, I just wondered about the following as a possible future extension of this proposal:
A module that implements named exports may infer the types of any imports that originate from the same module that provides the named export interface.
I am not sure about the syntax and how to implement this but maybe someone can come up with something smart.
Taking Remix as an example, this would enable e.g.
Then, in a route
This is a slightly different use case than the one outlined in the initial Next.js example, where the inference of
posts === Post[]does not rely on any additional imports.mohsen1 commentedon Jan 19, 2022
Daniel Rosenwasser (@DanielRosenwasser) just let me know that it's possible to import self to simulate what we're asking for. Just in case you're curious this is how it would look like in Next.js for instance:
This is less automatic than typing all exports using something like
export implementsbut it's possible to do today.ivanhofer commentedon Mar 25, 2022
I would also love to see such a feature in TypeScript.
Probably every project that has file conventions would benefit from such a feature. My project
typesafe-i18ncould also benefit from it.I don't think that there is a new syntax needed. What if TypeScript could use a
.d.tsfile to check what variables and exports a file should have?Currently e.g. a
file.d.tswould tell every other file that importsfile.jswhat values it exports and what shape those variables and functions have. But in thatfile.jsno type-check happens. Thefile.jscould also export something totally different and TypeScript would not throw an error:file.d.ts
file.js
So if TypeScript would use the information from the
d.tsfile to check if the.jsfile was implemented correctly, we could have a solution to the typed export problem. Of course this should also work with.tsfiles.With a solution like we can see in SvelteKit where types are generated and then linked with the real source files via the
rootDirscompiler option, those.d.tsfiles could be generated without adding declaration files inside the actualsrcfolder.21 remaining items
kentcdodds commentedon Dec 20, 2022
I like that. Though in Remix at least, you can very dynamically define what files count as routes so I would like an additional capability to define a type for the exports.
I'm a fan of what Jamie (@jamiebuilds-signal) suggests with the satisfies keyword.
camjackson commentedon Dec 21, 2022
I see the benefit of that, especially the idea of it being enforced from the outside, because it's a framework-level expectation. My concern is that it makes the code much less discoverable. To me it's very important that a new developer can look at a piece of code, without any context, and be able to figure out what it does by following the breadcrumbs.
I prefer an API that explicitly establishes a link that says "this code that you're looking at implements this interface", and then you can click through to see what that interface is. As opposed to it being configured somewhere else, with no way of like, naturally discovering it from the code in question. It becomes something that you just have to know.
ggoodman commentedon Dec 21, 2022
That's a great point. Perhaps the relevant comparison here is what you have defined in your
tsconfig.jsonfor thecompilerOptions.liboption. This is something defined on the 'outside' that affects which ambient types will show up. For use-cases where you want to be explicit, you can opt into a triple-slash directive to make the dependency explicit.btoo commentedon Dec 21, 2022
allowing ambient module declarations to specify relative module names as an alternative to using tsconfig could be a more intuitive and/or powerful way to achieve this by way of using typescript (especially
satisfies) rather than jsonand this could work with wildcards as well
a more ambitious feature, module declarations themselves could satisfy types
ggoodman commentedon Dec 21, 2022
Oh nice, btoo that's very clever!
mohsen1 commentedon Dec 23, 2022
I like
export {} satisfies Typebecausesatisfiesworks better thanimplementsin this case. The only thing is the emittedexport {}in runtime space that might not be desired.EloB commentedon Jan 23, 2023
btoo Is that doable at the moment or is that a a feature request?
Refactoring all projet
mariusGundersen commentedon Jun 1, 2023
I would really like this too, and would be happy with
export {} satisfies Type, although some magic with tsconfig and wildcard paths would be very useful.A workaround while we wait for this feature is to import the module into itself and then check that what is imported satisfies some type. For example:
This is quite cumbersome though, since you need to specify the name of the file in the file, and you get the error in the satisfies line, not on the export line.
feat(ts): expose `MiddlewareConfig` interface (#61576)
nfroidure commentedon Dec 3, 2024
Would be nice to be able to create some
.d.tsfiles too with this:A bit like https://github.com/vercel/next.js/blob/canary/packages/next/image-types/global.d.ts but for module validation.
Whook relies a lot on named imports (to declare handlers / services / commands), it would be a significant improve for the developer experience.
juancampa commentedon Dec 26, 2024
We've been following this issue for years (from membrane.io). It would greatly simplify how our users interact with the platform.
Mohsen Azimi (@mohsen1) Feel free to add us to the list 🙏
Brennername commentedon Mar 21, 2025
I do not mind using the :: "operator" because that is common in java method references as well, and it clearly indicates you are dealing not with a call using the dot operator but a reference using the :: operator.
But as I read this I was thinking "Why is this not a feature?"
bgub commentedon Jul 9, 2025
Just following up on this issue! My favorite solution is Glen Maddern (@geelen)'s proposal (quoted example above). This would be incredibly useful and I'd be happy to help contribute.
AgentEnder commentedon Oct 17, 2025
Throwing Nx's name in the mix as frameworks / tools that would benefit from this