rogergl
rogergl

Reputation: 3781

Typescript: declare return type to be dependent on argument value?

I have the following function call:

this.get('barcode').scan();

Is it possible to define the get function in a way that the return type is determined by the value of the function argument. In my case 'get' returns the Barcode class if called with 'barcode' and the Mqtt class if called with 'mqtt'.

Upvotes: 8

Views: 2521

Answers (3)

Jørgen Tvedt
Jørgen Tvedt

Reputation: 1294

Yes, as of Typescript 3.2 you can use conditional types to do the trick:

const get = <T extends "barcode" | "mqtt">(s: T) =>
    s === "barcode" ?
        <T extends "barcode" ? {scan: () => string} : {pan: () => string}>{scan: () => "we are scanning"} :
        <T extends "barcode" ? {scan: () => string} : {pan: () => string}>{pan: () => "we are panning"}

get("barcode").scan() // OK
get("mqtt").pan()     // OK
get("barcode").pan() // Error

Note: I did not succeed in moving the casts to the function's return type - this will have to be put in another question for the real experts.

Upvotes: 1

Ryan Cavanaugh
Ryan Cavanaugh

Reputation: 221392

Yes, you can use overload on strings to do this. Example:

interface BarcodeScanner { scan(): number; }
interface Accelerometer { getAcceleration(): number; }

class MyClass {
    get(name: 'accelerometer'): Accelerometer;
    get(name: 'barcode'): BarcodeScanner;
    get(name: string): any; // Fallback string signature
    get(name: string): any { // Implementation signature, not visible
        /* ... */
        return undefined;
    }

    something() {
        let x = this.get('barcode');
        x.scan(); // OK
        let y = this.get('accelerometer');
        y.getAcceleration(); // OK
    }
}

Upvotes: 8

Paleo
Paleo

Reputation: 23772

It's not possible to statically determine a type in function of a (dynamic) string. But generics are made for these cases.

Example:

class MyClass {
    public get<T>(type: string): T {
        // ...
    }
    public anotherMethod() {
        this.get<Barcode>('barcode').scan();
    }
}

I'm not sure to understand the problem with Ember, but if the method get is dynamically inserted in the object, then maybe a solution like this:

class MyClass {
    public get: function<T> (type: string): T; // this is just a static declaration, there is no generated JS code here

    public anotherMethod() {
        this.get<Barcode>('barcode').scan();
    }
}

Upvotes: 2

Related Questions