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

Collision between types & classes that implement them #3356

Closed
Gozala opened this issue Feb 8, 2017 · 1 comment
Closed

Collision between types & classes that implement them #3356

Gozala opened this issue Feb 8, 2017 · 1 comment

Comments

@Gozala
Copy link
Contributor

Gozala commented Feb 8, 2017

Often times I would like to define the interface of my library with structured types like:

type RangeSelector = {
  type: "RangeSelector",
  startSelector:Selector,
  endSelector:Selector,
  refinedBy:?Selector
}

So that library functions could use those types in argument annotations, like

export const refineBy = (selector:RangeSelector, refinement:Selector):Selector => {
   ....
}

More often than not libraries also tend to have implementations of those via classes:

class RangeSelector {
  type = "RangeSelector"
  startSelector:Selector
  endSelector:Selector
  refinedBy:?Selector
  constructor(start:Selector, end:Selector, refinement:?Selector=null) {
    this.startSelector = start
    this.endSelector = end
    this.refinedBy = refinement
  }
}

So I often tend to end up with type definition and implementation of it, which leads to naming collisions. There are multiple ways around it:

  1. Rename RangeSelector class for instance to avoid collision. Which requires finding an appropriate name for one, but even then it's not actually encoded anywhere that class implements that type so you tend to only find incompatibilities once you've wrote exported function that claims to return type while returning a class instance and mismatch becomes apparent.
  2. Just use classes in place of types. This is also not ideal because that way your functions become dependent on those classes & can't accept structurally equivalent instances just because of the annotations.

I think it would be really great if there was a way to define a structured type / interface and a class implementing with a same name and decide whether to export type / interface, class or both in export statement. Here is how it may look:

type RangeSelector = {
  type: "RangeSelector",
  startSelector:Selector,
  endSelector:Selector,
  refinedBy:?Selector
}

class RangeSelector {
  type = "RangeSelector"
  startSelector:Selector
  endSelector:Selector
  refinedBy:?Selector
  constructor(start:Selector, end:Selector, refinement:?Selector=null) {
    this.startSelector = start
    this.endSelector = end
    this.refinedBy = refinement
  }
}

export const refineBy = (selector:RangeSelector, refinement:Selector):Selector => {
   ....
}

This is how I'd propose flow to handle this:

  1. Declare type / interface as per RangeSelector definition.
  2. Declare that RangeSelector class, that must comply with a declared RangeSelector if it is in the scope, otherwise declare RenageSelector class as type.
  3. Refer to RangeSelector as type from type position and as class from value position. There for selector argument of refineBy function will refer to RangeSelector type definition. If there was new RangeSelector(...) expression that would refer to class instead.
  4. If there is a export type {RangeSelector} that would export type / interface definition. If there is a export {RangeSelector} that would export class.

Alternative syntax maybe used, but what's important here that it addresses following issues:

  1. Compliance between type and intended implementation class can be checked early, without necessarily creating a function that would declare type but use class instead.
  2. It would not require naming same thing twice, which in my experience often leads to mistakes where one named was intended to be used instead of the other, further delaying discovery of the mismatch (shows up only when dependent library, uses function and passes structurally equivalent type while exported function used class in annotation instead of a type it meant to).
@vkurchatkin
Copy link
Contributor

IMO it makes no sense to introduce such convoluted behavior instead of just using different names

@SamChou19815 SamChou19815 closed this as not planned Won't fix, can't repro, duplicate, stale Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants