Не удается вызвать выражения, у типа которых отсутствует сигнатура вызова

Я получаю сообщение об ошибке типа при использовании адаптера, как в этом сокращенном примере (см. последнюю строку метода getGloryOfAnimal). Я озадачен, потому что, насколько я могу судить, типы полностью раскрыты.

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));

Я считаю, что мог бы написать интерфейс для двух адаптеров, а не создавать тип объединения (например, (T | U)), но в моем случае интерфейс был бы очень большим.

Мысли? Я застрял с созданием огромного общего интерфейса для адаптеров?


person bjnsn    schedule 10.10.2016    source источник


Ответы (1)


Причина этой ошибки в том, что тип adapter.getValue:

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

И у этого типа действительно отсутствует позывной.

Тип data:

ICheetah | ILion

Что помогло бы, если бы тип adapter.getValue был бы:

(d: ICheetah | ILion) => string

Мои вопросы: почему вы не используете классы? Тогда фактическими функциями будут методы класса, в которых известен тип.


Редактировать

Вы можете обойти эту ошибку, выполнив:

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)); 
}

Это также устраняет необходимость в castRawData.
Если вам все еще нужна безопасность типов, замените d на (d as ICheetah).

person Nitzan Tomer    schedule 10.10.2016
comment
У меня такая же проблема, если я делаю адаптеры вот так: class CheetahAdapter { endpoint: 'cheetah'; castRawData (d: any) { return d as ICheetah} getValue (d: ICheetah) { return d.pace; } } Я неправильно понимаю, что вы имеете в виду? - person bjnsn; 10.10.2016
comment
Возможно, вы предлагаете им наследовать от общего абстрактного класса? В моем контексте это сложно, так как существует множество конечных точек, которые по-разному перекрываются. Я хотел бы использовать адаптеры в различных случаях, когда я знаю, что подмножество этих адаптеров удовлетворяет необходимым условиям. - person bjnsn; 10.10.2016
comment
Я не могу сказать, что понимаю, что вы пытаетесь сделать, поэтому я не могу помочь вам больше, чем с обходным путем, для которого проверьте мой пересмотренный ответ. Если вы можете объяснить больше, возможно, можно найти лучшее решение. - person Nitzan Tomer; 10.10.2016