Получение возвращаемого типа функции, использующей дженерики

Отказ от ответственности: следуют чрезмерно упрощенные функции, я знаю, что они бесполезны

function thinger<T>(thing: T): T {
    return thing;
}

const thing = thinger({ a: "lol" });

thing.a;

Приведенный выше код отлично транслируется. Но мне нужно поместить результат thinger<T> в объект.

interface ThingHolder {
    thing: ReturnType<typeof thinger>;
}

const myThingHolder: ThingHolder = {
    thing: thinger({ a: "lol" }),
};

Однако я потерял информацию о своем типе, поэтому myThingHolder.thing.a не работает

Свойство 'a' не существует для типа '{}'

Итак, я попробовал следующее

interface ThingHolder<T> {
    thing: ReturnType<typeof thinger<T>>;
}

const myThingHolder: ThingHolder<{ a: string }> = {
    thing: thinger({ a: "lol" }),
};

Но typeof thinger<T> не является допустимым машинописным текстом.

Как я могу получить тип возвращаемого значения функции, которая имеет другой тип возвращаемого значения на основе дженериков?


person ed'    schedule 24.04.2018    source источник
comment
А что плохого в том, чтобы самому разобраться? interface ThingHolder<T> { thing: T }?   -  person jcalz    schedule 24.04.2018
comment
Как и в моем отказе от ответственности в начале сообщения, я понимаю, что в этом чрезмерно упрощенном примере я должен сделать это, но мой реальный вариант использования не так прост. У меня есть функция, принимающая много дженериков и возвращающая что-то более сложное, чем просто эти дженерики (но дженерики по-прежнему влияют на возвращаемый тип)   -  person ed'    schedule 24.04.2018
comment
Что в вашем сложном случае мешает вам явно выписать функцию сложного типа T?   -  person jcalz    schedule 24.04.2018
comment
Я мог бы написать его, но он был бы чрезвычайно подробным и требовал переписывания каждый раз при изменении функции. Честно говоря, даже если бы это было разумным решением, я все равно хотел бы получить ответ на этот вопрос.   -  person ed'    schedule 24.04.2018
comment
Краткий ответ будет: TypeScript не может этого сделать, потому что у него нет ни общих значений, высокодородные типы, ни _ 1_ для произвольных выражений.   -  person jcalz    schedule 24.04.2018
comment
Почему бы не создать псевдоним типа для сложного типа и не использовать его как в определении функции, так и в определении свойства интерфейса?   -  person jcalz    schedule 24.04.2018
comment
Это не моя функция, это библиотека. Меня интересует только ответ на мой вопрос, а именно способ получить тип возвращаемого значения функции, на тип возвращаемого значения которой влияют дженерики. В качестве альтернативы было бы достаточно объяснения, почему это невозможно. Если это невозможно, у меня есть более простые способы решения моего варианта использования. Но если есть возможность, я бы предпочел это сделать.   -  person ed'    schedule 24.04.2018


Ответы (2)


С таким же успехом я мог бы поставить это в ответ, хотя, похоже, это не удовлетворит ваши потребности. TypeScript в настоящее время не имеет общих значений, более высокие типы, ни typeof на произвольных выражениях. Дженерики в TypeScript в этом смысле «мелкие». Насколько я знаю, к сожалению, нет способа описать функцию типа, которая вставляет параметры типа в общие функции и проверяет результат:

// doesn't work, don't try it
type GenericReturnType<F, T> = F extends (x: T) => (infer U) ? U : never

function thinger<T>(thing: T): T {
  return thing;
}

// just {}, ????
type ReturnThinger<T> = GenericReturnType<typeof thinger, T>;

Поэтому все, что я могу для вас сделать, это предложить обходные пути. Самый очевидный обходной путь - использовать псевдоним типа для описания того, что возвращает thinger(), а затем использовать его в нескольких местах. Это «обратная» версия того, что вы хотите; вместо извлечения возвращаемого типа из функции вы строите функцию из возвращаемого типа:

type ThingerReturn<T> = T; // or whatever complicated type you have

// use it here
declare function thinger<T>(thing: T): ThingerReturn<T>;

// and here
interface ThingHolder<T> {
  thing: ThingerReturn<T>;
}

// and then this works ???? 
const myThingHolder: ThingHolder<{ a: string }> = {
  thing: thinger({ a: "lol" }),
};

Это помогает? Я знаю, что это не то, что вы хотели, но, надеюсь, это хотя бы один из возможных путей для вас. Удачи!

person jcalz    schedule 24.04.2018
comment
Несколько интересных статей по ссылкам, которыми вы поделились, спасибо! Принять ответ как (по крайней мере, с TS 2.8) кажется невозможным :( - person ed'; 25.04.2018
comment
На самом деле я только что опубликовал подробное решение по адресу stackoverflow.com/a/60846777, которое также может помочь здесь. - person Aidin; 25.03.2020
comment
Здесь я разместил альтернативное решение: stackoverflow.com/questions/50321419/ - person Valfar Developer; 15.11.2020

Вероятно, это решило бы проблему. Но вам нужно создать фальшивый класс. Это работает, потому что классы одновременно являются типами и объектами среды выполнения JS.

// generic function
// we want to get its result, but we cannot do it like
// ReturnType<typeof foo<T>()> // syntax error
const foo = <T,>(arg: T) => ({ test: arg });
// so, let's create a parametric class
class Wrapper<T> {
  // with the only method that uses our "foo"
  wrapped = (arg: T) => foo(arg);   
};
// due to the fact that class is a type we can use it as a type 
// with a generic parameter.
type GetFooResult<T> = ReturnType<Wrapper<T>['wrapped']>
type Bingo = GetFooResult<number>; // { test: number }

TS площадка ссылка. На основании этого ответа. Спасибо @Colin

person faiwer    schedule 22.01.2021