Lpc_dark
Lpc_dark

Reputation: 2942

Typescript defining types on a object that can produce a result

If i have an object as such

let obj = {
   property1:()=>{ return Date()} // eg it doesn't have to be a date
   property2:()=>{ return 1}   
}

Now i want to transform this to type

{
   property1:Date,
   property2:number
}

How would i go about defining this in typescript. Just about everything i end up doing just does get it right.

I know the properties so i know it's something like

type Transform<T> = Record<keyof T,?>

how do i get each individual property to be transformed so that the final object can be typed as well.

//Some may need a little bit more solid example

Lets say this is a react app.

let dependencies = {user:UserContext}:{[key:string]:React.Context<any>}

Now i can transform all of my react context to the actual instances inside the context using something like

Object.entries(contextObject).map(([key,context])=>{
   return {[key]:useContext(context)}
}).reduce((a,b)=>{
  return {...a,...b}
},{})

This object will be all of the properties transformed.

I take in a config object of sorts and transform the properties keeping everything the same,

This can be anything, converting some parameters to db tables, converting dependencies to add to a class, without actually having to create the instance

Doing it isn't hard, it's having it typed so that on the other end of the transformation, i know what the type of object has been transformed into.

Upvotes: 1

Views: 60

Answers (1)

Karol Majewski
Karol Majewski

Reputation: 25800

Use the built-in Record and ReturnType types:

/**
 * Apply the constraint of having functions as values.
 */
type Source = Record<string, () => any>;

/**
 * Map functions to their return types.
 */
type Transform<T extends Source> = {
    [Property in keyof T]: ReturnType<T[Property]>
}

/**
 * Make sure the argument fulfills the requirement.
 */
declare function transform<T extends Source>(source: T): Transform<T>;

Usage:

let source = {
   property1: () => { return new Date() },
   property2: () => { return 1 }   
}

/**
 * `property1` is an instance of `Date`, property2 is a `number`.
 */
const { property1, property2 } = transform(source);

The transform function can be implemented this way:

function transform<T extends Source>(source: T): Transform<T> {
    return Object
        .entries(source)
        .reduce(
          (cumulus, [key, value]) => Object.assign(cumulus, { [key]: value() }),
          Object.create({}),
        );
}

Upvotes: 3

Related Questions