Sebas R.
Sebas R.

Reputation: 126

Infer Object Generic from its propery (return function type)

I want to make an interface that can infers a type T from the return value of the function data so the function step can receive as a parameter the type of this.

Something like this:

type DefInstance = {
    
    x : number,
    y : number,
};

type StateInstance<T> = {
    data : () => T,
    step : (this : DefInstance & T, turn : number) => boolean,
};

const instance : StateInstance<{
    f : number
}> = {
    data : () => ({
        f : 0,
    }),
    step : function(turn){
        this.f++;
        
        if(this.f > 3){
            this.x += 1;
            this.f = 0;
        }
        console.log(`move on turn ${turn}`);          
        return true;
    }
}

But without having to specify the type.

/*
  stuff from above
*/

//so it looks like this
const instance : StateInstance = {
    data : () => ({
        f : 0,
    }), //here i define the function and the return value
    step : function(turn){
        this.f++; //here i access the members described in the return value of data
        
        if(this.f > 3){
            this.x += 1;
            this.f = 0;
        }
        console.log(`move on turn ${turn}`);          
        return true;
    }
}

similar to what is made in Vue.js (v3) with the data function, where all methods can access the members of the state of the component

/*vue stuff*/
export default defineComponent({
  data : () => ({ count : 10 }),
  methods : {
    countUp() { this.count++; } //Autocompleted property `count` when access `this`
  }
})

I know that Vue uses a function (that uses lots of generics behind), but i want to know how to to do it using only a type (or interface) (if posible). Or, if its only doable using functions, how to do it with a function.

Upvotes: 0

Views: 91

Answers (1)

geoffrey
geoffrey

Reputation: 2444

If you need to reuse that magic function vera mentioned, you can make the type constructor generic:

import { apply } from 'free-types'

const of = <$T extends Type<1>>() => <T>(i: apply<$T, [T]>) => i;

const State = of<$StateInstance>();

const instance = State({
    data : () => ({
        f : 0,
    }), //here i define the function and the return value
    step : function(turn){
        this.f++; //here i access the members described in the return value of data
        
        if(this.f > 3){
            this.x += 1;
            this.f = 0;
        }
        console.log(`move on turn ${turn}`);          
        return true;
    }
})

$StateInstance can be defined like so

interface $StateInstance extends Type<1> {
   type: StateInstance<this[0]>
}

Or, if you don't need StateInstance for anything else:

interface $StateInstance extends Type<1> {
    type: { [K in 'data'|'step']: this[K] }
    data : () => this[0],
    step : (this : DefInstance & this[0], turn : number) => boolean,
};

playground

Upvotes: 1

Related Questions