Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript: constructor overloading

I need to translate these Java constructor overloads to Typescript:

public QueryMixin() {
    this(null, new DefaultQueryMetadata(), true);
}

public QueryMixin(QueryMetadata metadata) {
    this(null, metadata, true);
}

public QueryMixin(QueryMetadata metadata, boolean expandAnyPaths) {
    this(null, metadata, expandAnyPaths);
}

public QueryMixin(T self) {
    this(self, new DefaultQueryMetadata(), true);
}

public QueryMixin(T self, QueryMetadata metadata) {
    this(self, metadata, true);
}

public QueryMixin(T self, QueryMetadata metadata, boolean expandAnyPaths) {//...}

I've tried create these constructors taking a look over there, but I've not been able to figure out how to get it...

Any ideas?

constructor();  <<<1>>>
constructor(metadata: QueryMetadata);
constructor(metadata: QueryMetadata, expandAnyPaths: boolean);
constructor(self: T);
constructor(self: T, metadata: QueryMetadata);
constructor(selfOrMetadata: T | QueryMetadata, expandAnyPaths: boolean) {
    this.self = selfOrMetadata;  <<< PROBLEM HERE
    this.metadata = selfOrMetadata;  <<< PROBLEM HERE
    this.expandAnyPaths = expandAnyPaths;
}

On <<<1>>> I'm getting this compilation message:

Overload signature is not compatible with function implementation.

like image 809
Jordi Avatar asked Nov 28 '17 20:11

Jordi


1 Answers

You can achieve the constructor overloads you are after, but you have to perform some type checking to make it happen... I don't know what QueryMetadata looks like, but you should be able to get the idea from this self-contained example.

I am using a custom type guard to separate those union types:

interface QueryMetadata {
    metadata: string;
}

function isQueryMetadata(obj: any): obj is QueryMetadata {
    return (obj && obj.metadata);
}

class Example<T> {

    private metadata: QueryMetadata;
    private self: T;
    private expandAnyPaths: boolean;

    constructor(metadata: QueryMetadata);
    constructor(metadata: QueryMetadata, expandAnyPaths: boolean);
    constructor(self: T);
    constructor(self: T, metadata: QueryMetadata);
    constructor(selfOrMetadata: T | QueryMetadata, expandOrMetadata: boolean | QueryMetadata = false) {
        if (isQueryMetadata(selfOrMetadata)) {
            this.metadata = selfOrMetadata;
        } else {
            this.self = selfOrMetadata;
        }

        if (isQueryMetadata(expandOrMetadata)) {
            this.metadata = expandOrMetadata;
        } else {
            this.expandAnyPaths = expandOrMetadata;
        }
    }
}

Factory Method Alternative

If you are writing something new, you might prefer to use a factory method to construct your class...

interface QueryMetadata {
    metadata: string;
}

class Example<T> {

    private metadata: QueryMetadata;
    private self: T;
    private expandAnyPaths: boolean;

    protected constructor() { }

    static fromMetadata<T>(metadata: QueryMetadata, expandAnyPaths: boolean = false) {
        const example = new Example<T>();
        example.metadata = metadata;
        example.expandAnyPaths = expandAnyPaths;
    }

    static fromSelf<T>(self: T, expandAnyPaths: boolean = false) {
        const example = new Example<T>();
        example.self = self;
        example.expandAnyPaths = expandAnyPaths;
    }
}

const example1 = Example.fromSelf('Some Value');
const example2 = Example.fromSelf('Some Value', true);
const example3 = Example.fromMetadata({ metadata: 'val' });
const example4 = Example.fromMetadata({ metadata: 'val' }, true);
like image 175
Fenton Avatar answered Sep 26 '22 22:09

Fenton