I know this was asked like 5 years ago but I decided to explore how I might solve this if I needed to do this today. I was able to achieve what you are asking for with this approach which is broken down for conceptual purposes:
const baseFunction = (foo: string) =>
new Promise<string>(
(resolve) => resolve(foo)
)
type BaseFunctionType = typeof baseFunction
type AdditionalFunctionPropTypes = [
bar: string
]
type CombinedParams = [
...Parameters<BaseFunctionType>,
...AdditionalFunctionPropTypes
]
type DerivedFunction = (...combinedParams:CombinedParams) => ReturnType<BaseFunctionType>
Note - As of this writing (TSv5.4), there is currently no way to access the names of the function parameters. However, you can get their types via the Parameters<...> utility type. This allows for the preservation of their signatures but not their names.
As an aside, the Parameters<...> utility type does produce a labeled tuple with parameter names in it but these labels are purely aesthetic and can't currently be accessed. See this SO question and this GitHub issue for more info on that.
Taking the implementation above a bit further, it can be condensed down into a generic type like so:
type GenericDerivedFunction<BaseFunctionType extends (...args: any) => any, AdditionalFunctionPropTypes extends any[]> =
(...combinedParams:[...Parameters<BaseFunctionType>, ...AdditionalFunctionPropTypes]) => ReturnType<BaseFunctionType>
Then, you can type a new function as follows:
const baseFunction = (foo: string) =>
new Promise<string>(
(resolve) => resolve(foo)
)
const derivedFunction: GenericDerivedFunction<typeof baseFunction, [bar: string]> =
(foo, bar) => new Promise((resolve) => resolve(`${foo} ${bar}`))
If you wanted to refine the generic even slightly more to get rid of some of the any assertions and make it more concise, you can refactor using infer:
type ExtendFn<BaseFnT, AddPT extends any[]> = BaseFnT extends (...a: infer P) => infer R
? (...a: [...P, ...AddPT]) => R
: never
Caveat - As previously noted, this does not enforce the preservation of property names, only their signatures. So, the derivedFunction here could be instantiated with (other, names) which would pass the type checks and simply assign the derived types to those parameters in order. However, the type checker will still complain if too many parameters are supplied until the typing definition is adjusted. Note that the code hint will still display the property names though.
What a great little exercise to completely kill my productivity for things I should actually have been working on instead 🤣