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
It would be useful when a module can implement an interface using the implements keyword. Syntax: module MyModule implements MyInterface { ... }.
Example:
interfaceShowable{show(): void;}functionaddShowable(showable: Showable){}// This works:
module Login{exportfunctionshow(){document.getElementById('login').style.display='block';}}addShowable(Login);// This doesn't work (yet?)
module MenuimplementsShowable{exportfunctionshow(){document.getElementById('menu').style.display='block';}}addShowable(Menu);
That's a good question. I don't know which syntax would be the best, but here are a few suggestions:
implementsShowable;// I would prefer this one.
module implementsShowable;exportimplementsShowable;
It should only be allowed on external modules that don't use an export assignment, since if you use an export assignment, the thing that you export can already have an implements on another place.
What do you do with merged declarations, should you enforce the interface on the aggregate of all declarations? and what happens if they do not match in visibility?
e.g.:
module Foo{exportinterfaceIBar{(a:string): void;}export module BarimplementsIBar{// should this be an error?exportinterfaceInterface{}}functionBar(a: string) : void{}// not exported}varbar: Foo.IBar=Foo.Bar;
It should be allowed on ambient external modules. For these modules two syntaxes should be allowed in my opinion:
declare module "first"implementsFoo{}declare module "second"{interfaceBar{}exportimplementsBar;// this syntax is necessary, with the first syntax you can't reference Bar. }
Or should Bar be in the scope in an implements clause before the opening {?
Adding type info to an import statement isn't really useful in my opinion, since you can add the type info to the module itself.
And for merged declarations, I'd say that the module block that contains the implements clause should implement the interface. That also prevents issues with visibility.
Got it. Thanks, @nthypes. Adding on to this, the variable named __TYPE_CHECKING__ is arbitrary. You can call it anything you want. At first, I thought there might be some magical/special meaning associated with it, but that's not the case. Here's my updated example with some comments that elaborate on what I believe is happening based on the code you provided.
/*Inside the file commands.ts@note we need to import the same file we're in (`commands.ts`) in the module type checking section below*/// this defines the interface for our module (commands.ts), i.e., what it must exportexportinterfaceCommandsModuleInterface{// put whatever you want here for your use case.// This use case says we must export a value named `foo` of the type `string`foo: string;}// here, we implement our interfaceexportconstfoo="hello";/* -------------------------------------------------------------------------- *//* Below this line is the "hack" to validate our command.ts module./* It works by importing the `type` of the module (file) we're in, i.e., `commands.ts`/* -------------------------------------------------------------------------- */// this infers the type of what we're *actually* exporting from `commands.ts`// we store this as a type called `_` (again this is arbitrary)type_=typeofimport('./commands');// The line below here is where the actual type checking occurs.// We assign an arbitrarily named `const` as `__MODULE_TYPE_CHECK__`// and specify the type as our desired module interface, `CommandsModuleInterface`.// We assign the `const` `CommandsModuleInterface` a value of `{}` but immediately// try to type assert that`__MODULE_TYPE_CHECK__`, which we said should be of type// `CommandsModuleInterface`, matches the type actually exported by our module// and assigned the type `_`.// @note The eslint-disable-next-line is optional. I needed it for my lint rules// eslint-disable-next-line @typescript-eslint/no-unused-varsconst__MODULE_TYPE_CHECK__: CommandsModuleInterface={}as_;/* ----------------------------------- --- ---------------------------------- */
First thing we have to do is creating a module interface. It can be exported from a specific file as type or used in the same file that functions will be implemented.
Everything inside a module interface must be implemented when module implements is used.
Then you can import (as a type) this module interface into another file and use module implements to implement the interface. This can be a line module implements MyInterface or something with a body, like:
The module that is using module implements must implement all functions defined in module interface. Otherwhise, TypeScript should throw an error.
Shouldn't be a problem to implement other functions that are not defined in a module interface, as long as we implement at least every function defined in module interface.
We can possibly make some functions optional (not required to this suggestion):
// file user-module-interface.tsexport module interfaceUserModuleInterface{asyncfunctiongetUser(id: string): Promise<User>// createUser is optionalasyncfunctioncreateUser?(): Promise<void>}// ---------------// file prisma-user.tsimporttype{UserModuleInterface}from"./user-module-interface"
module implementsUserModuleInterfaceexportasyncfunctiongetUser(id){}// no errors, even if `createUser` is not implemented
💻 Use Cases
In this proposal, we do not rely on runtime features. All types (module interface and module implements) can be safely removed in build time.
The functions defined inside a module that extends a module interface can be auto-inferred.
And with this implementation, we have tree-shaking, because we can import only the functions we'll use.
To do something similar today, we have to create a class or an object and export the whole object, even if we want to use only one function.
Fernando Daciuk (@fdaciuk) Definitely think that modules implementing interfaces / types would warrant a special syntax to do away with the clunky workarounds presented in the thread. However, why would we need a special module interface construct for that and could not just use the normal interface / type keywords we already have?
I don't think we need Rüdiger Busche (@JarnoRFB). Maybe just a simple interface will solve the problem. module interface was just a suggestion to give more semantic, but like you said, probably module implements is enough =)
And you want to ensure that all those 4 files are exposing the same interface for the whole module (and not that 1 forgot to expose a function, or implemented a different type)
Reacted by xpdmk, Sébastien Lorber, Michael Telatynski, Juan Campa, BlueGreenMagick and Fernando Daciuk
Was just looking for this capability, the use case I ran into was implementing a plugin that is typed by the external consumer. In my exact case, what I wanted to do was something like:
Activity
sophiajt commentedon Aug 15, 2014
How would this work with external modules? It's likely once people can use it for internal, they'll also want to use it with external.
ivogabe commentedon Aug 15, 2014
That's a good question. I don't know which syntax would be the best, but here are a few suggestions:
It should only be allowed on external modules that don't use an export assignment, since if you use an export assignment, the thing that you export can already have an
implementson another place.RyanCavanaugh commentedon Aug 15, 2014
Approved. We prefer the syntax
and agreed that this is unneeded for files
export =assignments.mhegazy commentedon Aug 15, 2014
Some more questions:
e.g.:
ivogabe commentedon Aug 15, 2014
It should be allowed on ambient external modules. For these modules two syntaxes should be allowed in my opinion:
Or should Bar be in the scope in an implements clause before the opening
{?Adding type info to an import statement isn't really useful in my opinion, since you can add the type info to the module itself.
And for merged declarations, I'd say that the module block that contains the implements clause should implement the interface. That also prevents issues with visibility.
jbondc commentedon Apr 14, 2015
How would this be related to #2159? A namespace implements an interface?
DanielRosenwasser commentedon Apr 14, 2015
Jon (@jbondc) If we had this, it would also apply to namespaces. You should think of internal modules and namespaces as isomorphic.
49 remaining items
antl3x commentedon Nov 24, 2022
Eric Masiello (@ericmasiello) the implementation is wrong.
You should not edit
__TYPE_CHECKING__variable value. Only the type to check.Should error (wrong type):
Should error (no export):
Should pass:
PS: I also added a more "semantical" version on my original post.
ericmasiello commentedon Nov 24, 2022
Got it. Thanks, @nthypes. Adding on to this, the variable named
__TYPE_CHECKING__is arbitrary. You can call it anything you want. At first, I thought there might be some magical/special meaning associated with it, but that's not the case. Here's my updated example with some comments that elaborate on what I believe is happening based on the code you provided.antl3x commentedon Nov 24, 2022
Also if you want auto-completion with intellisense:
or
alloy commentedon Dec 2, 2022
This is great. I've amended the example slightly to not emit an empty runtime object:
lorenzodallavecchia commentedon Dec 2, 2022
Too bad you have to hard-code the file name in the file itself. There is a high chance of it getting misaligned after renaming/copying.
antl3x commentedon Dec 2, 2022
If this happens you will receive an type error.
feat: add full `NodeJS.ReadableStream` support
antl3x commentedon Aug 5, 2023
UPDATE: I'm using a more generic implementation to reduce the amount of boilerplate per file. For those interested:
Of course, you can change it to a name that makes the most sense to you:
satisfies|assert|implements.fdaciuk commentedon Apr 9, 2024
This is a pretty old issue, but so relevant! Would be nice to have this feature in TypeScript :D
I created the issue #58029, but since it's the same subject, I'll give my 2 cents here:
My proposal is adding a way to define a module interface, and a way to use it, adding the
module interfaceandmodule implementskeywords.📃 Motivating Example
Explaining the example
First thing we have to do is creating a
module interface. It can be exported from a specific file astypeor used in the same file that functions will be implemented.Everything inside a
module interfacemust be implemented whenmodule implementsis used.Then you can import (as a
type) thismodule interfaceinto another file and usemodule implementsto implement the interface. This can be a linemodule implements MyInterfaceor something with a body, like:We can also implement more than one interface, like in classes:
Rules
The module that is using
module implementsmust implement all functions defined inmodule interface. Otherwhise, TypeScript should throw an error.Shouldn't be a problem to implement other functions that are not defined in a
module interface, as long as we implement at least every function defined inmodule interface.We can possibly make some functions optional (not required to this suggestion):
💻 Use Cases
In this proposal, we do not rely on runtime features. All types (
module interfaceandmodule implements) can be safely removed in build time.The functions defined inside a module that extends a
module interfacecan be auto-inferred.And with this implementation, we have tree-shaking, because we can import only the functions we'll use.
To do something similar today, we have to create a
classor an object and export the whole object, even if we want to use only one function.JarnoRFB commentedon Apr 16, 2024
Fernando Daciuk (@fdaciuk) Definitely think that modules implementing interfaces / types would warrant a special syntax to do away with the clunky workarounds presented in the thread. However, why would we need a special
module interfaceconstruct for that and could not just use the normal interface / type keywords we already have?fdaciuk commentedon Apr 16, 2024
I don't think we need Rüdiger Busche (@JarnoRFB). Maybe just a simple
interfacewill solve the problem.module interfacewas just a suggestion to give more semantic, but like you said, probablymodule implementsis enough =)fdaciuk commentedon May 21, 2024
Daniel Rosenwasser (@DanielRosenwasser) this issue is 10yo. Do you think there is any chance of this being implemented in TypeScript soon?
fdaciuk commentedon Jul 9, 2024
Maybe Ryan Cavanaugh (@RyanCavanaugh) can help answer this =)
Ayc0 commentedon Nov 22, 2024
I'd like to bump this for another use case: if you have a
package.jsonwith in it,exports:{ "exports": { "node": "./file.node.ts", "require": "./file.require.ts", "module": "./file.module.ts", "default": "./file.default.ts" } }And you want to ensure that all those 4 files are exposing the same interface for the whole module (and not that 1 forgot to expose a function, or implemented a different type)
AgentEnder commentedon Oct 17, 2025
Was just looking for this capability, the use case I ran into was implementing a plugin that is typed by the external consumer. In my exact case, what I wanted to do was something like:
Instead, I ended up doing