Получение возвращаемого типа функции

У меня есть следующая функция:

function test(): number {
    return 42;
}

Я могу получить тип функции, используя typeof:

type t = typeof test;

Здесь t будет () => number.

Есть ли способ получить возвращаемый тип функции? Я бы хотел, чтобы t был number вместо () => number.

Ответы

Ответ 1

Нет способа сделать это (см. https://github.com/Microsoft/TypeScript/issues/6606 для отслеживания рабочих элементов, добавляя это).

Общим решением является написать что-то вроде:

var dummy = false && test();
type t2 = typeof dummy;

Ответ 2

Ответ Ryan больше не работает, к сожалению. Но я изменил его с помощью взлома, который я необоснованно рад. Вот:

const fnReturnType = (false as true) && fn();

Он работает путем литья false для литерального значения true, так что система типа считает, что возвращаемое значение является типом функции, но когда вы действительно запускаете код, это короткие замыкания на false.

TypeScript является лучшим.: D

Ответ 3

Невозможно получить возвращаемый тип функции, не выполняя ее печально. Это связано с тем, что когда TypeScript скомпилируется в JS, вы теряете всю информацию о типе.

Ответ 4

Код ниже работает без выполнения функции. Он из библиотеки react-redux- typescript (https://github.com/alexzywiak/react-redux-typescript/blob/master/utils/redux/typeUtils.ts)

interface Func<T> {
    ([...args]: any, args2?: any): T;
}
export function returnType<T>(func: Func<T>) {
    return {} as T;
}


function mapDispatchToProps(dispatch: RootDispatch, props:OwnProps) {
  return {
    onFinished() {
      dispatch(action(props.id));
    }
  }
}

const dispatchGeneric = returnType(mapDispatchToProps);
type DispatchProps = typeof dispatchGeneric;

Ответ 5

Если рассматриваемая функция является методом пользовательского класса, вы можете использовать декораторы методов в сочетании с Отразить метаданные, чтобы определить тип возвращаемого значения (функция конструктора) во время выполнения (и с ним делать, как сочтете нужным).

Например, вы можете записать его на консоль:

function logReturnType(
    target: Object | Function,
    key: string,
    descriptor: PropertyDescriptor
): PropertyDescriptor | void {
    var returnType = Reflect.getMetadata("design:returntype", target, key);

    console.log(returnType);
}

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

class TestClass {
    @logReturnType // logs Number (a string representation)
    public test(): number {
        return 42;
    }
}

Однако существует несколько заметных ограничений для этого подхода:

  • вам нужно явно указать тип возврата для метода, оформленного как такового, иначе вы получите undefined из Reflect.getMetadata,
  • вы можете ссылаться только на фактические типы, которые также существуют после компиляции; то есть никаких интерфейсов или дженериков

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

--emitDecoratorMetadata --experimentalDecorators

Ответ 6

Вариант некоторых предыдущих ответов, которые я использую, который работает в strictNullChecks и немного скрывает логику вывода:

function getReturnType<R>(fn: (...args: any[]) => R): R {
  return {} as R;
}

Использование:

function foo() {
  return {
    name: "",
    bar(s: string) { // doesn't have to be shorthand, could be `bar: barFn` 
      return 123;
    }
  }
}

const _fooReturnType = getReturnType(foo);
export type Foo = typeof _fooReturnType; // type Foo = { name: string; bar(s: string): number; }

Он вызывает функцию getReturnType, но не выполняет функцию оригинала. Вы можете предотвратить вызов getReturnType, используя (false as true) && getReturnType(foo), но IMO это просто делает его более запутанным.

Я просто использовал этот метод с некоторым regexp find/replace, чтобы перенести старый проект Angular 1.x, который имел функции ~ 1500 factory, написанные таким образом, первоначально в JS, и добавил типы Foo etc все использует... удивительный сломанный код, который вы найдете.:)

Ответ 7

Я придумал следующее, которое, кажется, работает красиво:

function returnType<A, B, Z>(fn: (a: A, b: B) => Z): Z
function returnType<A, Z>(fn: (a: A) => Z): Z
function returnType<Z>(fn: () => Z): Z
function returnType(): any {
    throw "Nooooo"
}

function complicated(value: number): { kind: 'complicated', value: number } {
    return { kind: 'complicated', value: value }
}

const dummy = (false as true) && returnType(complicated)
type Z = typeof dummy