5

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.

4
  • 3
    After a list of overloads you must supply the actual implementation signature, which must be (roughly speaking) an intersection of all the overloads that precede it. So in this case you must add at the bottom: constructor(selfOrMetaData?: T | QueryMetadata ...) and so on. Alternatively you can just drop the pretense of having any type safety at that point and just do constructor(,,,args: any[]). Commented Nov 28, 2017 at 20:40
  • I've added last java-like constructor implementation. I don't quite figure out how to distinct whether selfOrMetadata i assignable to self or metadata. Commented Nov 28, 2017 at 21:06
  • You need to make both arguments optional (selfOrMetaData?), since there's at least one overload where neither argument is supplied. Also, your second argument similarly needs to be a union of all possible argument types in the second position. expandAnyPathsOrMetadata?: boolean | QueryMetadata. Commented Nov 28, 2017 at 21:16
  • Would you mind posting me an answer? Commented Nov 28, 2017 at 21:20

1 Answer 1

6

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);
Sign up to request clarification or add additional context in comments.

1 Comment

I've created a new post in order to created a related question on here. The difference is that the last constructor has to be able to collect all possibilities.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.