0
votes

I'm receiving a type error when using an adapter as in this reduced example (see last line of method getGloryOfAnimal). I'm puzzled because, as far as I can tell, the types are fully explicated.

interface ICheetah {
    pace: string;
}

interface ILion {
    mane: string;
}

let LionAdapter = {
    endpoint: 'lion',
    castRawData: (d: any) => d as ILion,
    getValue: (d: ILion) => d.mane
}

let CheetahAdapter = {
    endpoint: 'cheetah',
    castRawData: (d: any) => d as ICheetah,
    getValue: (d: ICheetah) => d.pace
}

type AnimalAdapter = typeof CheetahAdapter | typeof LionAdapter;

function getDataFromEndpoint(endpoint: string): any {
    // data comes back in a format from the server
    // synchronous here for simplicity
    if (endpoint === 'cheetah') {
        return {
            pace: 'lightning speed'
        };
    } else {
        return {
            mane: 'shiny mane'
        };
    }
}

function getGloryOfAnimal(adapter: AnimalAdapter): string {
    let data = adapter.castRawData(getDataFromEndpoint(adapter.endpoint));
    // type error below:
    // 'cannot invoke expression whose type lacks a call signature'
    return adapter.getValue(data); 
}

console.log(getGloryOfAnimal(LionAdapter));

I believe I could write an interface for the two adapters rather than creating a union type (e.g. (T | U)), but in my case the interface would be very large.

Thoughts? Am I stuck with creating a huge common interface for the adapters?

1

1 Answers

0
votes

The reason for this error is that the type of adapter.getValue is:

((d: ICheetah) => string) | ((d: ILion) => string)

And this type indeed lacks a call signature.

The type of data is:

ICheetah | ILion

Which would have helped if the type of adapter.getValue would have been:

(d: ICheetah | ILion) => string

My questions is why don't you use classes? Then the actual functions will be class methods in which the type is known.


Edit

You can get around this error with doing:

let LionAdapter = {
    endpoint: 'lion',
    getValue: (d: any) => d.mane
}

let CheetahAdapter = {
    endpoint: 'cheetah',
    getValue: (d: any) => d.pace
}

function getGloryOfAnimal(adapter: AnimalAdapter): string {
    return adapter.getValue(getDataFromEndpoint(adapter.endpoint)); 
}

Which also removes the need for the castRawData.
If you still want type safety then replace d to (d as ICheetah).