Issue
I'm attempting to generate function signatures based on a inputted generic readonly object. Here is an example:
type input0 = ReadOnly<{x: number, y: number, z: number}>
// generate this type from input0 as a generic
type input0signature = (x: number, y: number, z: number) => void
and another for clarity:
type input1 = ReadOnly<{id: number, age: number}>
// generate this type from input1 as a generic
type input1signature = (id: number, age: number) => void
Please note that the order and names of the arguments for the generated function signatures need to be EXACTLY the same as the inputted type. Also, I'm not interested in generating signatures that use the spread operators (a tuple or array) for the arguments, or the inputted type as an argument, like so:
type input1 = ReadOnly<{id: number, age: number}>
// I do NOT want signatures to use the spread operator
type input1signatureTuple = (...args: [number, number]) => void
// Also NOT this
type input1signatureObject = (arg: input1) => void
Is this possible to do in typescript??
Solution
So for anyone who stumbles upon this later, I've found a hacky solution to the above problem - It's not perfect but works well enough for my needs. Here's a link to the full playground solution.
First you extract the inputted object's keys into a tuple. Then you map the created tuple into a tuple of a dummy interface (call it "Argument", "Property", whatever), which takes two generics a "name" and "type", and extends all possible argument types.
// this is the type you want to derive the function signature for
type BaseType = Readonly<{ x: number, y: number, z: number }>
// extract object keys into a tuple
type BaseTypeTuple = UnionToTuple<keyof BaseType>
// create an dummy interface that extends all potential types
// of your function arguments. In my case, i'm only using numbers
interface Arg<_name extends string, _type> extends Number {}
// map key names and types to a tuple of your dummy interface
type BaseTypeArgs<T, K extends Array<keyof T>> = {
[index in keyof K]: K[index] extends string
? Arg<K[index], T[K[index]]>
: never
}
// type should read: (args_0: Arg<"x", number>, args_1: Arg<"y", number>, args_2: Arg<"z", number>) => void
// with correct ordering and types
const testFn = (...args: BaseTypeArgs<BaseType, BaseTypeTuple>) => {}
An interface is used because typescript preserves interface aliases thereby allowing you to inject the "name" of the function arguments, without fear of them being erased. In other words, your IDE's intellisense will almost always include the name and generics of your interface in every signature it's used in.
Answered By - moomoolive
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.