Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal - Module Interface #58029

Closed
6 tasks done
fdaciuk opened this issue Apr 2, 2024 · 6 comments
Closed
6 tasks done

Proposal - Module Interface #58029

fdaciuk opened this issue Apr 2, 2024 · 6 comments
Labels
Duplicate An existing issue was already created

Comments

@fdaciuk
Copy link

fdaciuk commented Apr 2, 2024

πŸ” Search Terms

module interface, interface for modules, module implements interface

βœ… Viability Checklist

⭐ Suggestion

Module Interface Proposal

I found some old suggestions to this feature, but I think none is clean like this I'm proposing.

The main idea is give the ability to create an interface to a module. Something like was proposed in #420, but different: my proposal is adding a way to define a module interface, and a way to use it, adding the module interface and module implements keywords.

πŸ“ƒ Motivating Example

// file user-module-interface.ts
export module interface UserModuleInterface {
  async function getUser(id: string): Promise<User>
  async function createUser(): Promise<void>
}

// ---------------

// file prisma-user.ts
import type { UserModuleInterface } from "./user-module-interface"

module implements UserModuleInterface

export async function getUser(id) {}

export async function createUser() {}

// ---------------

// another-file.ts
import { getUser, createUser } from "./prisma-user"

Explaining the example

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:

// file prisma-user.ts
import type { UserModuleInterface } from "./user-module-interface"

module implements UserModuleInterface {
  export async function getUser(id) {}
  export async function createUser() {}
}

We can also implement more than one interface, like in classes:

// file article-module-interface.ts
export module interface ArticleModuleInterface {
  function getArticles(): Promise<Article[]>
}

// ---------------

// file prisma-user.ts
import type { UserModuleInterface } from "./user-module-interface"
import type { ArticleModuleInterface } from "./artictle-module-interface"
module implements UserModuleInterface, ArticleModuleInterface

export async function getUser(id) {}

export async function createUser() {}

export async function getArticles() {}

Rules

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.ts
export module interface UserModuleInterface {
  async function getUser(id: string): Promise<User>
  // createUser is optional
  async createUser?(): Promise<void>
}

// ---------------

// file prisma-user.ts
import type { UserModuleInterface } from "./user-module-interface"

module implements UserModuleInterface

export async function getUser(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.

@MartinJohns
Copy link
Contributor

MartinJohns commented Apr 2, 2024

Note that the module keyword is on the edge of being deprecated: #57913

To me this just sounds like a duplicate of #420, just an alternative approach (which usually does not warrant a new issue and should be a comment). Both issues are about the very same problem.

@snarbies
Copy link

snarbies commented Apr 2, 2024

module is only being deprecated as an alias for namespace, right? This looks like ESM which isn't going anywhere.

Well, I guess this syntax is a bit confusing as it's neither an internal module nor ESM...

module implements UserModuleInterface {
  export async function getUser(id) {}
  export async function createUser() {}
}

The rest appears to be ESM.

@fdaciuk
Copy link
Author

fdaciuk commented Apr 2, 2024

@snarbies I just need to say that that specific module is implementing an interface. I don't know if the suggested syntax is good or not, but it was the only thing that came to mind at the moment =)

And yes: the proposal is to maintain the ESM syntax, and just add module interface and module implements to improve type checking in functions without the need for a class or object, and also without adding anything at runtime .

@DanielRosenwasser DanielRosenwasser added the Duplicate An existing issue was already created label Apr 9, 2024
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Apr 9, 2024

It does feel like #420 roughly captures what's being described here, so I'm going to mark this as a duplicate.

@fdaciuk
Copy link
Author

fdaciuk commented Apr 9, 2024

@DanielRosenwasser should I comment this proposal in #420?

@DanielRosenwasser
Copy link
Member

Yeah, I'd try to keep discussion there going forward.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

4 participants