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

Intersection of constructor signatures #3916

Closed
sophiajt opened this issue Jul 17, 2015 · 6 comments
Closed

Intersection of constructor signatures #3916

sophiajt opened this issue Jul 17, 2015 · 6 comments
Labels
By Design Deprecated - use "Working as Intended" or "Design Limitation" instead

Comments

@sophiajt
Copy link
Contributor

Was just chatting with @JsonFreeman about how we might model mixins, and we came across an interesting snag.

Let's say we want to make something that represents the combination of two classes:

class C {
  constructor(public a: number) {}
}

class D {
  constructor(public b: string) {}
}

var cd: typeof C & typeof D;
var result = new cd(??);
var my_a = result.a;
var my_b = result.b;

Class C has one requirement, a: number, and class D has another requirement, b: string. Currently what we do is to turn the construct signatures into an overload that takes either an a or a b. I'm not sure this makes sense, because in effect it's turning the requirements into optional parameters. You can't construct cd in a way that both a and b are valid.

Should parameters to the constructor, which act as its requirements for correct construction, instead be merged differently?

@RyanCavanaugh
Copy link
Member

What's a plausible implementation for a function that takes C and D and creates cd ?

@danquirk
Copy link
Member

What do actual mixin libraries/implementations do here?
Do people actually mixin types with disjoint constructors like this in some meaningful way?

@sophiajt
Copy link
Contributor Author

@RyanCavanaugh and @danquirk - optionally you could create something that's a mixin style function:

function mixin<E extends {new(a: number): any}, F extends {new(b: string): any}>(e: E, f: F) : E & F {
  return class {
    constructor(a: number, b: string) {
      var result1 = new e(a);  
      var result2 = new f(b);

      return merge(result1, result2);
    }
  }
}

Notice that the merged constructor requires both parameters since it needs each one to construct the constituent parts.

(obviously a full example would mix the static sides, too)

@wgebczyk
Copy link

Please change subject as this is more sum than intersection.

@JsonFreeman JsonFreeman added the By Design Deprecated - use "Working as Intended" or "Design Limitation" instead label Jul 18, 2015
@JsonFreeman
Copy link
Contributor

I've thought about this issue more, and the current behavior actually seems correct in a way. If you think about the type of cd in your example, it's something like this:

new (a: number): C;
new (b: string): D;

This does have two overloads, and it should because if the type is simultaneously both C and D (the static sides), it is capable of constructing something in either of two ways. If you use the first construct signature, you get a C, and if you use the second, you get a D.

I think we were confusing this with a different type, which would have been wrong, namely:

new (a: number): C & D;
new (b: string): C & D;

This type says that no matter which of the two options you choose for constructing the object, you will get something that is both a C and a D. This would indeed be wrong.

Put differently, @jonathandturner used the word "requirement". I would consider construct signatures requirements with of the instance side of a class, but I would consider them capabilities of the static side of a class.

I will close this, as I think the current behavior makes perfect sense.

@jbondc
Copy link
Contributor

jbondc commented Sep 23, 2015

class cat {
  constructor(public feed: number) {}
  meow()
}

class dog {
  constructor(public feed: string) {}
  woof()
}

I'd say both assumptions are wrong:

 // doesn't tell you anything about what the intersection of  'dog' or 'cat' means. 
let catDog: cat & dog;

 // if you feed a 'catDog' a number, maybe it returns a `cat`
let feedANumber = new catDog(1);

// if you feed a 'catDog' a string, maybe it returns a `dog`
let feedAString = new catDog("steak"); 

// if you feed a 'catDog' a number or a string, maybe it returns a `catDog`
let feedANumber2 = new catDog(2); 
let feedAString2 = new catDog("steak");

You just don't know.

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
By Design Deprecated - use "Working as Intended" or "Design Limitation" instead
Projects
None yet
Development

No branches or pull requests

6 participants