luq
luq

Reputation: 53

Type returned by method based on constructor optional param

I have a class with constructor:

class Example<T> {
    constructor(private elem: T, private array?: T[]) {}
}

and I want add method some with returned:

How can I do this?

Actualy I have:

class Example<T, S extends T[] | undefined> {
    constructor(private elem: T, private array?: S) {}

    some(): S extends undefined ? Promise<T> : Promise<S> {
        if(!this.array) {
            return Promise.resolve(this.elem);
        }

        return Promise.resolve(this.array);
    }
}

const a = new Example(1, undefined);
a.some(); // ✓ Return Promise<number>

const b = new Example(1, [1, 2, 3]);
b.some(); // ✓ Return Promise<number[]>

const c = new Example(1);
c.some(); // ✗ Return Promise<number> | Promise<number[]>, should return Promise<number>

Upvotes: 3

Views: 90

Answers (2)

luq
luq

Reputation: 53

After some time I found a solution:

class Example<T, S extends T[] | undefined = undefined> {
    constructor(private elem: T, private array?: S) {}

    some(): S extends undefined ? Promise<T> : Promise<S> {
        if(!this.array) {
            return Promise.resolve(this.elem) as any; // [1]
        }

        return Promise.resolve(this.array) as any; // [1]
    }
}

const a = new Example(1, undefined);
a.some(); // ✓ Return Promise<number>

const b = new Example(1, [1, 2, 3]);
b.some(); // ✓ Return Promise<number[]>

const c = new Example(1);
c.some(); // ✓ Return Promise<number>

const d = new Example(1, ["a", "b"]); // ✓ Should be error here
d.some();

[1] Because of issues with conditional type returned type we need to use as any casting

More elegant should be solution proposed by hackape but it not cover d case where we expect error

Upvotes: 0

hackape
hackape

Reputation: 19957

  1. remove the extends constraint from type parameter S
  2. invert S extends undefined ? to S extends T[] ?
class Example<T, S> {
    constructor(private elem: T, private array?: S) {}

    some(): S extends T[] ? Promise<S> : Promise<T> {
        if (!this.array) {
            // @ts-ignore
            return Promise.resolve(this.elem);
        }
        // @ts-ignore
        return Promise.resolve(this.array);
    }
}

const a = new Example(1, undefined);
a.some(); // ✓ Return Promise<number>

const b = new Example(1, [1, 2, 3]);
b.some(); // ✓ Return Promise<number[]>

const c = new Example(1);
c.some(); // ✓ Return Promise<number>

Playground Link

Upvotes: 1

Related Questions