¿Cómo distinguir `(() => any) | undefined` de simplemente `() => any` en TypeScript?
Tengo dos tipos: (() => any) | undefined y () => any. Me gustaría extraer el tipo de retorno de la función, pero solo si el valor definitivamente no es undefined.
He intentado lo siguiente:
type IsUndefined<T> = T extends (() => any) ? "no" : "yes";
Pero esto se resuelve en "yes" | "no" cuando el tipo no es undefined. Quiero detectar la diferencia entre estos tipos sin crear una unión.
Por favor, vea este enlace del área de pruebas como ejemplo.
Esa es la historia corta, la historia larga es que tengo una estructura como la siguiente:
type MyStruct = {
optionalHook?: ((...args: any) => any) | undefined,
requiredHook: (...args: any) => any,
}
Me gustaría extraer el tipo de retorno del hook opcional, pero solo cuando exista. Me gustaría extraer el tipo de retorno del hook obligatorio en caso contrario.
Ve este enlace del patio de juegos para un ejemplo más completo.
Respuesta #1
No estoy seguro de por qué la ausencia de optionalHook no infiere T como undefined en createStruct, pero he logrado obtener el resultado deseado agregando parámetros genéricos por defecto:
// Create the generic struct
type MyStruct<T extends ((...args: any) => any) | undefined, U extends (...args: any) => any> = {
optionalHook?: T,
requiredHook: U,
}
// Utility function for easily creating stucts with the correct type.
// Without this I'd have to type the generic arguments manually for every struct instance.
function createStruct<T extends ((...args: any) => any) | undefined = undefined, U extends (...args: any) => any = (...args: any) => any>(struct: MyStruct<T, U>) {
return struct;
}
const withOptional = createStruct({
optionalHook: () => 5,
requiredHook: () => "hello",
});
const withoutOptional = createStruct({
requiredHook: () => true,
});
// The function for extracting the return type
type getReturnType<T> =
T extends MyStruct<infer O, infer R> ?
O extends (...args: any) => infer OR ?
OR :
R extends (...args: any) => infer RR ? RR : never :
never;
type ResultWithOptional = getReturnType<typeof withOptional>;
type ResultWithoutOptional = getReturnType<typeof withoutOptional>;
Área de juego de TypeScript
Establecer explícitamente optionalHook en undefined también funciona:
Área de juego de TypeScript
Respuesta #2
Puedes usar una utilidad personalizada como esta:
export const isSet = <T>(value: T | null | undefined): value is T => {
return value != null;
};
Y luego úsalo:
let something; // (() => any) | undefined
if (isSet(something)) {
doSomething(something); // () => any
}
Y si necesitas lo contrario (es decir, comprobar que un tipo es indefinido), entonces ni siquiera necesitas una utilidad personalizada, puedes usar lodash: ya sea isUndefined o isNil (= es indefinido o nulo).
