32

I'm trying to discover a pattern for combining multiple interfaces into one abstract class. Presently I can combine multiple interfaces via implements, but an interface cannot declare a constructor. When I must introduce a constructor I'm forced to use an abstract class. When I use a abstract class I must re-declare the entire composite interface! Surely I'm missing something?

interface ILayerInfo {
    a: string;
}

interface ILayerStatic {
    b(): string;
}

class Layer implements ILayerInfo, ILayerStatic {
    constructor(info: ILayerInfo);
    a: string;
    b(): string;
}

ANSWER: Use new:

interface Layer extends ILayerInfo, ILayerStatic {
    new(info: ILayerInfo);
}

// usage: new Layer({ a: "" });
Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Corey Alix
  • 2,694
  • 2
  • 27
  • 38
  • Maybe an InstanceType could be useful here somehow. Reference: https://github.com/Microsoft/TypeScript/issues/25998 – Shah Apr 04 '22 at 18:59

2 Answers2

49

Declaring a constructor on the same interface as the instance members doesn't really make much sense -- if you're going to pass in a type dynamically to use in a constructor, it's the static side of the class that would be restricted. What you would want to do is probably something like this:

interface Colorable {
    colorize(c: string): void;
}

interface Countable {
    count: number;
}

interface ColorCountable extends Colorable, Countable {
}

interface ColorCountableCreator {
    new(info: {color: string; count: number}): ColorCountable;
}

class ColorCounted implements ColorCountable {
    count: number;
    colorize(s: string) { }
    constructor(info: {color: string; count: number}) {
        // ...
    }
}

function makeThings(c: ColorCountableCreator) {
    var results: ColorCountable[];
    for(var i = 0; i < 10; i++) {
        results.push(new c({color: 'blue', count: i}));
    }
    return results;
}

var items = makeThings(ColorCounted);
console.log(items[0].count);

See also How does typescript interfaces with construct signatures work?

Community
  • 1
  • 1
Ryan Cavanaugh
  • 209,514
  • 56
  • 272
  • 235
  • It was the "new" keyword on the interface that I was missing! I didn't want the class at all. With "new" I can stick to interfaces. – Corey Alix Jul 25 '13 at 19:32
2

Here's a sample of a solution for inheriting multiple interfaces:

type ShowAllWithTypography =  TypographyClampProps & ShowAllProps

interface Props extends ShowAllWithTypography
Shah
  • 2,126
  • 1
  • 16
  • 21
  • 1
    Yes, or you could do `interface Props extends TypographicClampProps, ShowAllProps` but that does not help with class inheritance, which does not allow for multiple base classes. – Corey Alix Feb 07 '22 at 16:13
  • I think see what you're saying. This solution was for expecting props types in a react project, where I wanted to expect certain props in Child components, but extend the properties of my Props interface a bit further. This worked for me but your use case needs a constructor, yes? – Shah Feb 07 '22 at 16:27
  • I was using libraries written before typescript that used mixins as a way of implementing multiple inheritance. I needed to describe them, specifically the constructor. I used "new" in the interface to describe the constructor. I think your answer is addressing the question in a way I'm not understanding. It seems you're missing a "new" declaration. – Corey Alix Feb 07 '22 at 17:35
  • Anything given the Props return type will have all the properties of ShowAllWithTypography. I could extend it further by using a {} after my last line. No ‘new’ keyword needed to assign a Type to an object. New keyword is good for calling a constructor, if my memory serves me accurately. – Shah Feb 08 '22 at 05:27