Отримання типу повернення функції


100

Я маю таку функцію:

function test(): number {
    return 42;
}

Я можу отримати тип функції, використовуючи typeof:

type t = typeof test;

Ось, tбуде () => number.

Чи є спосіб отримати тип повернення функції? Я хотів би tбути numberзамість () => number.


2
Ні, у будь-якому разі не з типом. typeof поверне лише "функцію" ( мій корпус може бути неправильним ).
Ігор

Було б чудово, якби це стало можливим. Іноді ти визначаєш типи, коли виштовхуєш їх із функції, і не існує (приємного) способу захопити цю структуру.
Йорген Тведт,

Відповіді:


163

РЕДАГУВАТИ

Станом на 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


52
Ісусе, це жахливо.
Джон Вайс

для потоку: const DUMMY = ((null: будь-який): Object) && fn () тип експорту Type = typeof DUMMY
David Xu

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

1
@NSjonas вам потрібно сказати vscode використовувати іншу версію. Я думаю, що на рядку стану внизу праворуч.
Meirion Hughes

12
Я повинен був зробити ReturnType<typeof fn>(відповідь передбачає ReturnType<fn>достатньо).
spacek33z

49

Найпростіший спосіб у TypeScript 2.8:

const foo = (): FooReturnType => {
}

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

5

Наведений нижче код працює без виконання функції. Це з бібліотеки response-redux-typecript ( 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 ви маєте рацію, наприклад , не запропонував більше не працює, хоча ви можете легко обдурити машинопис з !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)але IMO це лише робить його більш заплутаним.

Я щойно використав цей метод із деяким пошуком / заміною регулярного виразу, щоб перенести старий проект Angular 1.x, який мав ~ 1500 заводських функцій, написаних таким чином, спочатку в JS, і додав Fooтипи etc для всіх застосувань ... дивовижний непрацюючий код знайдуть. :)


Дякую! Хоча це сумно, це вимагає такої кількості багатослів’я, принаймні це працює!
екманав

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

Однак у цього підходу є кілька помітних обмежень:

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

Крім того, вам потрібно буде вказати наступні аргументи командного рядка для компілятора TypeScript, оскільки і декоратори, і відображувані метадані є експериментальними характеристиками на момент написання цього допису:

--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.