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


101

У меня есть такая функция:

function test(): number {
    return 42;
}

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

type t = typeof test;

Вот и tбудет () => number.

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


2
Нет, во всяком случае, не с typeof. typeof вернет только «функцию» ( мой регистр может быть неправильным ).
Игорь

Было бы здорово, если бы это стало возможным. Иногда вы определяете типы, когда выталкиваете их из функции, и нет (приятного) способа зафиксировать эту структуру.
Йорген Тведт

Ответы:


164

РЕДАКТИРОВАТЬ

Начиная с TypeScript 2.8 это официально возможно с ReturnType<T>.

type T10 = ReturnType<() => string>;  // string
type T11 = ReturnType<(s: string) => void>;  // void
type T12 = ReturnType<(<T>() => T)>;  // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]

Подробности смотрите здесь .

TypeScript потрясающий!


Олдскульный взлом

Ответ Райана, к сожалению, больше не работает. Но я модифицировал его с помощью хака, чему я безосновательно рад. Вот:

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

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

TypeScript - лучший. : D


53
Господи, это ужасно.
John Weisz

для потока: const DUMMY = ((null: any): Object) && fn () тип экспорта Type = typeof DUMMY
Дэвид Сюй,

любая идея, почему при установке 2.8 и попытке я получаю следующую ошибку Cannot find name 'ReturnType':? с использованием vscode
NSjonas

1
@NSjonas вам нужно указать vscode использовать другую версию. Я думаю, что строка состояния внизу справа.
Мейрион Хьюз

12
Пришлось ReturnType<typeof fn>(ответ подразумевает, ReturnType<fn>что достаточно).
spacek33z

49

Самый простой способ в TypeScript 2.8:

const foo = (): FooReturnType => {
}

type returnType = ReturnType<typeof foo>;
// returnType = FooReturnType

5

Приведенный ниже код работает без выполнения функции. Это из библиотеки response-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;

4

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

Обычный обходной путь - написать что-нибудь вроде:

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

3
Я не думаю, что этот обходной путь больше будет работать - вероятно, из-за анализа типов на основе потока управления.
JKillian

3
@JKillian, вы правы, предложенный пример больше не работает, хотя вы можете легко обмануть TypeScript !1вместо false- typescriptlang.org/play/…
DanielM

3

Изменить: это больше не требуется с TS 2.8! ReturnType<F>дает тип возвращаемого значения. См. Принятый ответ.


Вариант некоторых из предыдущих ответов, которые я использую, который работает 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)но ИМО это только сбивает с толку.

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


Благодарность! К сожалению, это требует такого количества словоблудия, но, по крайней мере, работает!
ecmanaut

@ecmanaut Нам это больше не нужно! В TS 2.8 вы можете использовать ReturnType<F>для получения типа возвращаемого значения функции. :)
Аарон Билл

Ответы здесь сильно отличаются от примера основы вопроса, что затрудняет понимание того, как создать тип, который является возвращаемым типом функции test. Этот ответ был одним из наиболее полезных только для переименования testв foo(и, по общему признанию, создания более сложного типа возврата для кайфов). И принятый ответ, и ваш комментарий, вероятно, хороши, если вы уже знаете, как применить их к исходному примеру. (Здесь поздняя ночь, и мне не сразу понятно, как будет выглядеть синтаксис полного примера ReturnType.)
ecmanaut

1

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

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

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,
  • вы можете ссылаться только на фактические типы, которые также существуют после компиляции; то есть никаких интерфейсов или дженериков

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

--emitDecoratorMetadata --experimentalDecorators

0

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


3
Хотя технически верно, что TS теряет информацию о типе во время выполнения, этот факт не имеет значения, потому что OP хочет определить тип функции во время компиляции. Это стало еще более очевидным теперь, ReturnType<T>когда это реализовано в TypeScript.
thedayturns

0

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

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

1
Я не думаю, что аргументы функции должны быть общими, поскольку вы все равно их выбрасываете. fn: (...args: any[]) => Zэто все, что тебе нужно.
Аарон Билл
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.