typescript - tutorial - Obteniendo el tipo de retorno de una función
tipos de datos en angular (8)
El siguiente código funciona sin ejecutar la función. Es de la biblioteca 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;
Tengo la siguiente función:
function test(): number {
return 42;
}
Puedo obtener el tipo de la función usando
typeof
:
type t = typeof test;
Aquí,
t
será
() => number
.
¿Hay alguna manera de obtener el tipo de retorno de la función?
Me gustaría que
t
sea
number
lugar de
() => number
.
La forma más fácil en TypeScript 2.8:
const foo = (): FooReturnType => {
}
type returnType = ReturnType<typeof foo>;
// returnType = FooReturnType
No hay forma de recuperar el tipo de retorno de la función sin ejecutarla tristemente. Esto se debe a que cuando TypeScript se compila en JS, pierde toda la información sobre el tipo.
No hay una manera de hacer esto (consulte https://github.com/Microsoft/TypeScript/issues/6606 para el seguimiento del elemento de trabajo que agrega esto).
Una solución común es escribir algo como:
var dummy = false && test();
type t2 = typeof dummy;
Se me ocurrió lo siguiente, que parece funcionar bien:
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
Si la función en cuestión es un método de una clase definida por el usuario, puede usar decoradores de métodos en conjunción con Reflect Metadata para determinar el tipo de retorno (función del constructor) en tiempo de ejecución (y con él, haga lo que le parezca).
Por ejemplo, puede iniciar sesión en la consola:
function logReturnType(
target: Object | Function,
key: string,
descriptor: PropertyDescriptor
): PropertyDescriptor | void {
var returnType = Reflect.getMetadata("design:returntype", target, key);
console.log(returnType);
}
Simplemente ajuste este decorador de métodos en un método de su elección y tendrá la referencia exacta a la función constructora del objeto que supuestamente se devuelve de la llamada al método.
class TestClass {
@logReturnType // logs Number (a string representation)
public test(): number {
return 42;
}
}
Sin embargo, existen algunas limitaciones notables para este enfoque:
-
debe definir explícitamente el tipo de retorno en un método decorado como tal; de lo contrario, no se
Reflect.getMetadata
desdeReflect.getMetadata
, - solo puede hacer referencia a tipos reales que también existen después de la compilación; es decir, sin interfaces o genéricos
Además, deberá especificar los siguientes argumentos de la línea de comandos para el compilador de mecanografía, porque tanto los decoradores como los metadatos reflejados son características experimentales al momento de escribir esta publicación:
--emitDecoratorMetadata --experimentalDecorators
EDITAR
A partir de TypeScript 2.8, esto es oficialmente posible con
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[]
Ver here para más detalles.
¡TypeScript es increíble!
Hack de la vieja escuela
La respuesta de Ryan ya no funciona, desafortunadamente. Pero lo modifiqué con un truco del que estoy irracionalmente feliz. Mirad:
const fnReturnType = (false as true) && fn();
Funciona al convertir falso en el valor literal de verdadero, de modo que el sistema de tipos piensa que el valor de retorno es el tipo de la función, pero cuando realmente ejecuta el código, corta los circuitos en falso.
TypeScript es el mejor. :RE
Editar: ¡
Esto ya no es necesario con TS 2.8!
ReturnType<F>
proporciona el tipo de retorno.
Ver respuesta aceptada.
Una variante de algunas de las respuestas anteriores que estoy usando, que funciona en
strictNullChecks
y oculta un poco la inferencia de gimnasia:
function getReturnType<R>(fn: (...args: any[]) => R): R {
return {} as R;
}
Uso:
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; }
Llama a la función
getReturnType
, pero
no
llama a la función original.
Puede evitar que la llamada
getReturnType
use
(false as true) && getReturnType(foo)
pero IMO esto solo lo hace más confuso.
Acabo de usar este método con algunos regexp buscar / reemplazar para migrar un antiguo proyecto Angular 1.x que tenía ~ 1500 funciones de fábrica escritas de esta manera, originalmente en JS, y agregué los tipos de
Foo
etc. a todos los usos ... increíble el código roto Uno lo encontrará.
:)