function
ReturnTypeSafe, ParametersSafe, Curried, Promisify.
import type {
ReturnTypeSafe, ParametersSafe, Curried, Promisify,
} from '@gentleduck/ttest/function'
Safer variants of the built-in ReturnType / Parameters (they degrade to never instead of erroring on non-callable input) plus two function transforms. For class constructors see class; for async-aware return types see async.
ReturnTypeSafe
type ReturnTypeSafe<F> = F extends (...args: any[]) => infer R ? R : never
Return type of F, or never for non-functions. Unlike the built-in ReturnType<T>, this does not require T extends (...args: any) => any at the call site, so it composes safely inside larger conditionals.
type a = ReturnTypeSafe<() => string> // string
type b = ReturnTypeSafe<(n: number) => boolean> // boolean
type c = ReturnTypeSafe<string> // never
type d = ReturnTypeSafe<undefined> // never
// Compose without prior callable check.
type ResultOf<F> = ReturnTypeSafe<F> | 'no-fn'
type a = ResultOf<() => 1> // 1 | 'no-fn'
type b = ResultOf<number> // 'no-fn' (never collapses out of the union)
ParametersSafe
type ParametersSafe<F> = F extends (...args: infer P) => any ? P : never
Parameter tuple, or never for non-functions.
type a = ParametersSafe<(x: number, y: string) => void> // [x: number, y: string]
type b = ParametersSafe<() => 1> // []
type c = ParametersSafe<{ kind: 'data' }> // never
Edge case: overloaded function types collapse to the last overload's parameters — this matches the built-in Parameters behavior.
Curried
type Curried<F>
Curried form: an n-ary function becomes a chain of unary functions.
type f = (a: number, b: string, c: boolean) => Date
type c = Curried<f>
// ^? (a: number) => (b: string) => (c: boolean) => Date
// Zero-arg and unary functions are pass-through.
type a = Curried<() => 1> // () => 1
type b = Curried<(x: number) => 1> // (x: number) => 1
Promisify
type Promisify<F> = F extends (...args: infer P) => infer R ? (...args: P) => Promise<R> : never
Wrap the return type of F in Promise. Use to derive the async signature of a sync API surface, or to type a .then-able adapter.
type f = (n: number) => string
type p = Promisify<f> // (n: number) => Promise<string>
type Sync = { read(): string; write(s: string): void }
type Async = { [K in keyof Sync]: Promisify<Sync[K]> }
// ^? { read: () => Promise<string>; write: (s: string) => Promise<void> }
Edge case: Promisify<F> on a function that already returns Promise<T> produces (...args: P) => Promise<Promise<T>>. Use Awaited or DeepAwaited on the result if you need flat promises.