Можно ли установить общий тип функции, не вызывая ее в Typescript?

Вопрос в названии. Ниже пример того, чего я пытаюсь достичь:

const apiResponse = Promise.resolve({data: 1} as {data: any}); // Assume I don't know the type of data
const mapData = <T>(res: {data: T}): T => res.data;

const x = apiResponse.then(mapData<number>); // This one fails to compile
const y = apiResponse.then(mapData); // Succeeded but y is of type any
const z = apiResponse.then(_ => mapData<number>(_)); // Succeeded but verbose !

person Alexandre Annic    schedule 11.12.2019    source источник
comment
Вы можете указать параметр типа универсального типа функции только при его вызове. _ => f<T>(_), вероятно, лучшее, на что вы можете здесь надеяться.   -  person jcalz    schedule 11.12.2019
comment
Возможный дубликат Специализация типа для универсальной функции   -  person jcalz    schedule 11.12.2019


Ответы (2)


Лично я не вижу ничего плохого в вашем "многословном" звонке

const z = apiResponse.then(_ => mapData<number>(_)); 

но если вы хотите что-то еще, читайте дальше:


Универсальные типы функций, в которых одна функция может действовать как функция нескольких типов, позволяют указывать параметр универсального типа только при его вызове. Ваша функция mapData имеет следующий тип:

type GenFunc = <T>(res: { data: T; }) => T
const verifyMapData: GenFunc = mapData; // okay

Обратите внимание, что GenFunc не является псевдонимом универсального типа; это псевдоним определенного типа, который относится к общему функциональному типу. Вы можете думать о параметре типа T как о «принадлежащем» сигнатуре вызова, а не имени функции или имени любого псевдонима типа функции. Поскольку оно не принадлежит имени функции, вы не можете написать mapData<T> отдельно, а поскольку оно не принадлежит имени типа GenFunc, вы также не можете написать GenFunc<T>.


Сравните это с другим, но связанным псевдонимом универсального типа, который относится к семейству конкретных типов функций:

type GenAlias<T> = (res: {data: T; }) => T

В этом типе параметр универсального типа «принадлежит» имени псевдонима типа, а не типу функции, который он представляет.

Теперь, к сожалению, верно то, что системе типов TypeScript в настоящее время не хватает выразительности, необходимой для того, чтобы взять тип GenFunc и автоматически сгенерировать тип GenAlias<T>. По крайней мере, сейчас вам нужно вручную написать определение типа GenAlias<T>, как я только что сделал.


Что компилятор может сделать, так это распознать, что значение типа GenFunc можно присвоить переменной типа GenAlias<T> для любого конкретного T, которое вы хотите, поэтому следующее компилируется просто отлично:

const mapDataNumber: GenAlias<number> = mapData; // okay

И поэтому вы можете получить следующее для компиляции без дополнительного вызова функции, если это имеет значение:

const w = apiResponse.then(mapDataNumber); // Promise<number>

Кроме того, если вы не хотите тратить строку на присвоение переменной, вы можете использовать утверждение типа для немного меньшей безопасности типов, но абсолютно без эффекта времени выполнения:

const v = apiResponse.then(mapData as GenAlias<number>); // Promise<number>

Любой из них должен работать на вас.


Хорошо, надеюсь, это поможет. Удачи!

Link to code

person jcalz    schedule 11.12.2019
comment
Спасибо за такой качественный ответ. Это второй раз, когда вы спасаете меня тезисы 2 последних дней ;-). - person Alexandre Annic; 12.12.2019

        const apiResponse = Promise.resolve( { data: 1, asType: ( data as any ).__proto__} as { data: any, asType: any } );
        const mapData = <T>( res: { data: any, asType: { new( ...args ): T } } ): T => {
            if ( res.data ) {
                res.data.__proto__ = res.asType;
                return res.data;
            }

            return undefined;
        }
        const y = apiResponse.then( mapData );

Это то, что вам нужно?

person Derek Lawrence    schedule 11.12.2019
comment
Я не думаю. Я абсолютно не понимаю, что вы пытаетесь сделать: D. - person Alexandre Annic; 12.12.2019
comment
если в ответе вы передаете значение и фактический тип, вы можете вывести тип в ответе, назначив объект прототипа типов объекту значения - person Derek Lawrence; 12.12.2019