00213-hard-vue-basic-props

Back

declare function VueBasicProps<P, D, C, M>(
  options: VueOptions<P, D, C, M>
): unknown;


type GetComputed<T> = {
  [P in keyof T]: T[P] extends () => infer Res ? Res : T[P];
};

type MaybeReturnType<T> = T extends (...args: any) => infer R
  ? R
  : T extends new (...args: any) => infer R1
  ? R1
  : T;
  
type GetProps<T> = {
  [P in keyof T]: T[P] extends {
    type: infer Prop;
  }
    ? Prop extends any[]
      ? MaybeReturnType<Prop[number]>
      : MaybeReturnType<Prop>
    : Equal<T[P], {}> extends true ? any : MaybeReturnType<T[P]>;
};
type VueOptions<P, D, C, M> = {
  props: P;
  data: (this: GetProps<P>) => D;
  computed: C & ThisType<D & GetComputed<C>>;
  methods: M & ThisType<D & GetComputed<C> & M & GetProps<P>>;
};

Solution by Vampirelee #32612

type GetComputed<Obj> = {
  [key in keyof Obj]: Obj[key] extends () => infer R ? R : Obj[key]
}
type GetType<Ctor> = Ctor extends (...args: any[]) => infer R // for string and number to return type
  ? R
  : Ctor extends Array<() => unknown> // is Array
    ? ReturnType<Ctor[number]>
    : Ctor extends new (...args: any[]) => infer R // is Constructor
      ? R
      : Ctor
type GetProps<Obj> = {
  [key in keyof Obj]: Obj[key] extends { type: infer Ctor }
    ? GetType<Ctor>
    : {} extends Obj[key]
      ? any
      : GetType<Obj[key]>
}

declare function VueBasicProps<Props, Data, Computed, Methods>(options: {
  props: Props
  data: (this: GetProps<Props>) => Data,
  computed: Computed & ThisType<Data>,
  methods: Methods & ThisType<Data & GetComputed<Computed> & Methods & GetProps<Props>>
}): any

Solution by HoikanChan #32584

type Constructor<T> = T extends (infer U)[]
  ? Constructor<U>
  : T extends StringConstructor
  ? string
  : T extends NumberConstructor
  ? number
  : T extends BooleanConstructor
  ? boolean
  : T extends new (...args: any[]) => infer C
  ? C
  : any;

type Props<T> = {
  [K in keyof T]: T[K] extends { type: infer U }
    ? Constructor<U>
    : Constructor<T[K]>;
};

declare function VueBasicProps<T, D, C, M>(options: {
  props: T;
  data: (this: Props<T>) => D;
  computed: C & ThisType<D>;
  methods: M &
    ThisType<
      D &
        M & {
          [K in keyof C]: C[K] extends (...args: any[]) => any
            ? ReturnType<C[K]>
            : never;
        } & Props<T>
    >;
}): any;

Solution by vangie #32316

type AnyClass = new (...args: any[]) => unknown

type TransformComputed<T> = {
  readonly [K in keyof T]: T[K] extends (...args: any[]) => any ? ReturnType<T[K]> : never
}

type TransformProps<T> = {
  [K in keyof T]: T[K] extends { type: infer R extends AnyClass | AnyClass[] }
    ? MultipleToPrimitive<R>
    : T[K] extends AnyClass
      ? InstanceType<T[K]>
      : any
}

type ToPrimitive<T extends AnyClass, R = InstanceType<T>> = R extends {
  valueOf: () => infer S
}
  ? S
  : never

type MultipleToPrimitive<T extends AnyClass | AnyClass[]> = T extends AnyClass
  ? ToPrimitive<T>
  : T extends (infer R extends AnyClass)[]
    ? ToPrimitive<R>
    : never

type VueProps = Record<string, any>
type VueMethods = Record<string, (...args: any[]) => unknown>
type VueComputed = Record<string, Function>
type VueData = Record<string, unknown>

type VueBasicOptions<
  Props extends VueProps,
  Data extends VueData,
  Computed extends VueComputed,
  Methods extends VueMethods,
> = {
  props: Props
  data: (this: TransformProps<Props>) => Data
  computed: Computed & ThisType<TransformProps<Props> & Data & TransformComputed<Computed> & Methods>
  methods: Methods & ThisType<TransformProps<Props> & Data & TransformComputed<Computed> & Methods>
}

type VueBasicResult<
  Props extends VueProps,
  Data extends VueData,
  Computed extends VueComputed,
  Methods extends VueMethods,
> = TransformComputed<Props> & Data & Methods

declare function VueBasicProps<
  Props extends VueProps,
  Data extends VueData,
  Computed extends VueComputed,
  Methods extends VueMethods,
>(options: VueBasicOptions<Props, Data, Computed, Methods>): VueBasicResult<Props, Data, Computed, Methods>

/* _____________ Test Cases _____________ */
import type { Debug }  from '@type-challenges/utils'
import type { Equal }  from '@type-challenges/utils'
import type { Expect } from '@type-challenges/utils'
import type { IsAny }  from '@type-challenges/utils'

class ClassA {}

VueBasicProps({
  props: {
    propA: {},
    propB: { type: String },
    propC: { type: Boolean },
    propD: { type: ClassA },
    propE: { type: [String, Number] },
    propF: RegExp,
  },
  data(this) {
    type PropsType = Debug<typeof this>
    type cases = [
      Expect<IsAny<PropsType['propA']>>,
      Expect<Equal<PropsType['propB'], string>>,
      Expect<Equal<PropsType['propC'], boolean>>,
      // Expect<Equal<PropsType['propD'], ClassA>>,
      Expect<Equal<PropsType['propE'], string | number>>,
      Expect<Equal<PropsType['propF'], RegExp>>,
    ]

    // @ts-expect-error
    this.firstname
    // @ts-expect-error
    this.getRandom()
    // @ts-expect-error
    this.data()

    return {
      firstname: 'Type',
      lastname: 'Challenges',
      amount: 10,
    }
  },
  computed: {
    fullname() {
      return `${this.firstname} ${this.lastname}`
    },
  },
  methods: {
    getRandom() {
      return Math.random()
    },
    hi() {
      alert(this.fullname.toLowerCase())
      alert(this.getRandom())
    },
    test() {
      const fullname = this.fullname
      const propE = this.propE
      const prop = this.propB
      type cases = [Expect<Equal<typeof fullname, string>>, Expect<Equal<typeof propE, string | number>>]
    },
  },
})

/* _____________ Further Steps _____________ */
/*
  > Share your solutions: https://tsch.js.org/213/answer
  > View solutions: https://tsch.js.org/213/solutions
  > More Challenges: https://tsch.js.org
*/

type MultipleToPrimitiveOldVersion<T extends AnyClass | AnyClass[]> = T extends [
  infer S extends AnyClass,
  ...infer Rest extends AnyClass[],
]
  ? ToPrimitive<S> | MultipleToPrimitive<Rest>
  : T extends AnyClass
    ? ToPrimitive<T>
    : never


Solution by gearonix #31911



declare function defineStore<S,G,A>(store: Store<S,G,A>): A&S&RemapGetter<G>
type RemapGetter<T> = {
  [P in keyof T]: T[P] extends ()=> infer R ? R :never
}
type Store<State,G,A> ={
  state:()=>State
  getters:G&ThisType<Readonly<State>&RemapGetter<G>>
  actions:ThisType<State&A>&A,
  id:string
}

Solution by manyuemeiquqi #29465

213 - Vue Basic Props

Ok. You can try this one. This one picks up from yesterday's "Simple Vue" challenge and introduces the concept of "props". If you have (and understand) yesterday's challenge, then this one should be pretty approachable.

🎥 Video Explanation

Release Date: 2023-04-08 19:00 UTC

Vue Basic Props

🔢 Code

// ============= Test Cases =============
import type { Equal, Expect, IsAny } from './test-utils'

class ClassA {}

VueBasicProps({
  props: {
    propA: {},
    propB: { type: String },
    propC: { type: Boolean },
    propD: { type: ClassA },
    propE: { type: [String, Number] },
    propF: RegExp,
  },
  data(this) {
    type A1 = IsAny<typeof this['propA']>;
    type B1 = true;
    type C1 = Expect<Equal<A1, B1>>;

    type A2 = typeof this['propB'];
    type B2 = string;
    type C2 = Expect<Equal<A2, B2>>;
    
    type A3 = typeof this['propC'];
    type B3 = boolean;
    type C3 = Expect<Equal<A3, B3>>;
    
    type A4 = typeof this['propD'];
    type B4 = ClassA;
    type C4 = Expect<Equal<A4, B4>>;
    
    type A5 = typeof this['propE'];
    type B5 = string | number;
    type C5 = Expect<Equal<A5, B5>>;
    
    type A6 = typeof this['propF'];
    type B6 = RegExp;
    type C6 = Expect<Equal<A6, B6>>;

    // @ts-expect-error
    this.firstname
    // @ts-expect-error
    this.getRandom()
    // @ts-expect-error
    this.data()

    return {
      firstname: 'Type',
      lastname: 'Challenges',
      amount: 10,
    }
  },
  computed: {
    fullname() {
      return `${this.firstname} ${this.lastname}`
    },
  },
  methods: {
    getRandom() {
      return Math.random()
    },
    hi() {
      alert(this.fullname.toLowerCase())
      alert(this.getRandom())
    },
    test() {
      type A7 = typeof this.fullname;
      type B7 = string;
      type C7 = Expect<Equal<A7, B7>>;

      type A8 = typeof this.propE;
      type B8 = string | number;
      type C8 = Expect<Equal<A8, B8>>;
    },
  },
});

// ============= Your Code Here =============
type ResultType<T> =
  T extends (...args: any[]) => infer R
  ? R
  : T extends new (...args: unknown[]) => infer S
    ? S
    : any;

type PropsTypes<T> = {
  [P in keyof T]:
    T[P] extends { type: infer Result }
    ? Result extends readonly unknown[]
      ? ResultType<Result[number]>
      : ResultType<Result>
    : ResultType<T[P]>
};

declare function VueBasicProps<
  Props,
  Data,
  Computed extends
    Record<PropertyKey, (...args: unknown[]) => unknown>,
  Methods,
>(
  options: {
    props: Props,
    data(this: PropsTypes<Props>): Data,
    computed: Computed & ThisType<Data>,
    methods:
      & Methods
      & ThisType<
          & PropsTypes<Props>
          & Methods
          & {
            [P in keyof Computed]:
              ReturnType<Computed[P]>
          }
        >,
  }
): unknown;

➕ More Solutions

For more video solutions to other challenges: see the umbrella list! https://github.com/type-challenges/type-challenges/issues/21338

Solution by dimitropoulos #26114

type ReturnType<F> = F extends (...args: infer A) => infer R ? R : F;

type GetValues<C extends Record<PropertyKey, any>> =  { [K in keyof C]: ReturnType<C[K]> };

type Computed<C extends Record<PropertyKey, any>, T> = 
{ [K in keyof C]: (this: T, ...args: unknown []) => ReturnType<C[K]> };

type Methods<M extends Record<PropertyKey, any>, T> = 
{ [K in keyof M]: (this:T, ...args: unknown []) => ReturnType<M[K]> };

type Data<D> = (this: Data<D>, ...args: unknown[]) => D ;

declare function SimpleVue<
  D extends Record<PropertyKey, any>, 
  C extends Record<PropertyKey, () => any>, 
  M extends Record<PropertyKey, () => any>
>(options: {
  data: Data<D>,
  computed: Computed<C, D>,
  methods: Methods<M, D & GetValues<C> & Methods<M, D & GetValues<C>>>
}): Data<D> & Computed<C, D> & Methods<M, D & GetValues<C> & Methods<M, D & GetValues<C>>>;

Solution by JhonLandy #25874

type BaseType<B> = B extends new (...args: any) => infer R
  ? B extends { (value?: any): infer T }
    ? T
    : R
  : B;
type PropsParse<P> = {
  [Key in keyof P]: BaseType<
    P[Key] extends Record<PropertyKey, never>
      ? any
      : P[Key] extends { type: any }
      ? P[Key]["type"] extends any[]
        ? P[Key]["type"][number]
        : P[Key]["type"]
      : P[Key]
  >;
};

declare function VueBasicProps<
  D,
  C extends Record<PropertyKey, (...args: any) => any>,
  M,
  P
>(options: {
  props: P;
  data: (this: void & PropsParse<P>) => D;
  computed: C & ThisType<D & PropsParse<P>>;
  methods: M &
    ThisType<D & M & { [K in keyof C]: ReturnType<C[K]> } & PropsParse<P>>;
}): any;

Solution by ickynavigator #25334

// your answers
type ToBaseType<T> = T extends (infer K)[]
    ? ToBaseType<K>
    : T extends new (...args: any) => infer R
    ? T extends { (value?: any): infer B }
        ? B
        : R
    : T
type GetProps<T extends object> = {
    [P in keyof T]: T[P] extends { [x: string]: never }
        ? any
        : ToBaseType<T[P] extends { type: infer K } ? K : T[P]>
}
declare function VueBasicProps<
    T extends { [x: PropertyKey]: any },
    U extends { [x: string]: () => unknown },
    V extends { [x: string]: () => unknown },
    W extends { [x: string]: any }
>(options: {
    props: W
    data: (this: GetProps<W>) => T
    computed: U & ThisType<T>
    methods: V &
        ThisType<
            V &
                T & {
                    [P in keyof U]: U[P] extends () => infer R ? R : never
                } & GetProps<W>
        >
}): any

Solution by studymachiney #25128

type ToBaseType<T> = T extends (infer TT)[] ?   // [Type1, Type2, ...]
  ToBaseType<TT> :                              // ToBaseType<Type1 | Type2 | ...>
  (T extends new (...args: any) => infer I ?    // ClassConstructor => insType
    (T extends { (value?: any): infer B } ?     // TypeConstructor => baseType
      B :                                       // baseType
      I) :                                      // insType
    T);

/**Props ThisType */
type __PropsThisType<W> = { [P in keyof W]: W[P] extends { [x: string]: never } ? any :  //{}
  ToBaseType<W[P] extends { type: infer TT } ? TT : W[P]> };

declare function VueBasicProps<
  T extends { [x: PropertyKey]: any },
  U extends { [x: string]: () => unknown },
  V extends { [x: string]: () => unknown },
  W extends { [x: string]: any }>(options: {
    data: (this: __PropsThisType<W>) => T,
    computed: U & ThisType<T>,
    methods: V & ThisType<V & T & { [P in keyof U]: U[P] extends () => infer R ? R : never } & __PropsThisType<W>>,
    props: W
  }): any

Solution by E-uler #24701

// your answers
type PropConstructor<T> = 
|{new (...args:any[]): T&object} 
| {():T}

type PropType<T> = PropConstructor<T> | PropConstructor<T>[]

type Props<T> = PropType<T> | {type?:PropType<T>}

type inferPropType<T> = T extends Props<infer P> 
? unknown extends P
  ? any
  : P
:any

type inferPropsType<T> = {
  [K in keyof T]:inferPropType<T[K]>
}


type inferComputed<T  extends Record<string,any>> = {
  [K in keyof T]:ReturnType<T[K]>
}

declare function VueBasicProps<
P,
D,
C extends Record<string,any>,
M,
Props=inferPropsType<P>
>(options: {
 props:P,
 data:(this:Props)=>D
 computed:C&ThisType<Props&D&inferComputed<C>&M>
 methods:M&ThisType<Props&D&inferComputed<C>&M>
}): any

Solution by walker-hzx #24539


type Computed<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => infer R ? R : T[K]
}

type GetPropType<T> =
  T extends unknown[]
    ? GetPropType<T[number]>
    : T extends (...args: any) => any
      ? ReturnType<T>
      : T extends new (...args: any) => any
        ? InstanceType<T>
        : T

type Props<T> = {
  [K in keyof T]: T[K] extends Record<string, never>
    ? any
    : T[K] extends { type: infer R }
      ? GetPropType<R>
      : GetPropType<T[K]>
}

interface Options<D, C, M, P> {
  props: P
  data?: (this: Props<P>) => D
  computed?: C & ThisType<D & Computed<C> & M & Props<P>>
  methods?: M & ThisType<D & Computed<C> & M & Props<P>>
}

declare function VueBasicProps<D, C, M, P> (options: Options<D, C, M, P>): any

Solution by drylint #23167

// your answers
type ToBase<T> = T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BooleanConstructor ? boolean : T extends RegExpConstructor ? RegExp : T extends new (...args: any[]) => infer R ? R : T

type RecordFunction = Record<string, Function>;

type PropType = String | Array<any> | Record<string,any> | Number;

type PropsType = Record<string, {type: PropType | PropType[]} | PropType | PropType[]>

type ArrayToBase<T> = T extends any[] ? ToBase<T[number]> :  ToBase<T>

type TransProps<T extends PropsType> = {
  [key in keyof T]: {} extends T[key] ? any : 'type' extends keyof T[key] ?  ArrayToBase<T[key]['type']> : ArrayToBase<T[key]>
}

type TransComputed<T extends RecordFunction> = {
  [key in keyof T]: T[key] extends (...args: any[]) => infer R ? R : T
}

type Vue<
D extends Record<string, any>,
C extends RecordFunction,
M extends RecordFunction, 
P extends PropsType,
_TransProps = TransProps<P>,
_TransComputed = TransComputed<C> 
> = {
  data: (this: _TransProps) => D,
  computed: C & ThisType<D & M & _TransComputed &  _TransProps>,
  methods: M & ThisType<D & M & _TransComputed &  _TransProps>,
  props: P
}

declare function VueBasicProps<
D extends Record<string, any>,
C extends RecordFunction,
M extends RecordFunction, 
P extends PropsType,
>(options: Vue<D, C, M, P>): any

Solution by 437204933 #22292

// 乱七八糟,,, ParseProps 整了好久才把红线去掉,弄完感觉又学到了没用的姿势。。。
type ParseProps<P> = {
    [K in keyof P]
    : P[K] extends (_: any) => infer KK ? KK
    : P[K] extends { [_: keyof any]: never } ? any
    : P[K] extends { type: (() => infer KK) } ? KK
    : P[K] extends { type: (new () => infer KK) } ? KK
    : P[K] extends { type: (infer KK)[] }
    ? KK extends (_: any) => infer KKK ? KKK : '' : ''
}
type Computed<C> = { [K in keyof C]: C[K] extends (..._: any) => infer R ? R : C[K] }
type VueOption<P, D, C, M> = {
    props?: P;
    data?: (this: ParseProps<P>) => D
    computed?: C & ThisType<D>;
    methods
    ?: M & ThisType<
        Computed<{ [K in keyof C]: C[K] extends (..._: any) => infer R ? R : C[K] }>
        & D & M & ParseProps<P>
    >
}
declare function VueBasicProps<P, D, C, M>(options: VueOption<P, D, C, M>): VueOption<P, D, C, M>

Solution by goddnsgit #21877

type RecordFunction = Record<string, Function>;
type TransitionComputed<T extends RecordFunction> = {
  [P in keyof T]: T[P] extends () => infer A ? A : never;
};
type PropType = String | Array<any> | Record<string,any> | Number;
type PropsType = Record<string,{
  type?:PropType | Array<PropType>
} | PropType>
type mappingProps<T> =
T extends BooleanConstructor?
  boolean :
  T extends NumberConstructor ?
     number :
    T extends StringConstructor ?
      string  :
      T extends RegExpConstructor ?
        RegExp  :
        T extends new (...args: any) => infer R ? R :T;

type TransitionProps<T extends PropsType> = {
  [P in keyof T]:{} extends T[P] ? any : T[P] extends  {
    type:infer R
  } ? R extends Array<any> ? mappingProps<R[number]>: mappingProps<R> : mappingProps<T[P]>
}
type Vue<
  Data extends Record<string, any>,
  Computed extends RecordFunction,
  Methods extends RecordFunction,
  Props extends PropsType,
  _TransitionComputed = TransitionComputed<Computed>,
  _TransitionProps =TransitionProps<Props>,
> = {
  data?(this:_TransitionProps): Data;
  props:Props;
  computed?: Computed & ThisType<Methods & _TransitionComputed & Data & _TransitionProps>;
  methods?: Methods & ThisType<Methods & _TransitionComputed & Data & _TransitionProps>;
};

declare function VueBasicProps<
  Data extends Record<string, any>,
  Computed extends RecordFunction,
  Methods extends RecordFunction,
  Props extends PropsType
>(options: Vue<Data, Computed, Methods,Props>): any;

Solution by so11y #21111

type TInstance<T> =
  T extends { (...args: any): infer R } ? R :
  T extends abstract new (...args: any) => infer R ? R :
  T extends Record<string | number | symbol, never> ? any : 
  T extends (infer S)[] ? TInstance<S> :
  T extends { type: infer S } ? TInstance<S> : never;

type TComputed<T> = {[K in keyof T]: T[K] extends (...args: unknown[]) => infer R ? R : never};

type TProps<P> = {[K in keyof P]: TInstance<P[K]>};

declare function VueBasicProps<D, C, M, P>(options: {
  props: P,
  data: (this: TProps<P>) => D,
  computed: C & ThisType<D & TComputed<C> & TProps<P>>,
  methods: M & ThisType<D & TComputed<C> & M & TProps<P>>,
}): any

Solution by kenleung5e28 #20575

type PropType2Type<T> =
  T extends StringConstructor
    ? string
    : T extends NumberConstructor
      ? number
      : T extends BooleanConstructor
        ? boolean
        : T extends (infer TS)[]
          ? PropType2Type<TS>
          : T extends abstract new (...args: any) => any
            ? InstanceType<T>
            : T

type VueProps<P> = {
  [K in keyof P]: 
    P[K] extends Record<string,never>
      ? any
      : P[K] extends { type: infer PT }
        ? PropType2Type<PT>
        : PropType2Type<P[K]>
}

type VueComputed<C> = {
  [P in keyof C]: C[P] extends () => unknown ? ReturnType<C[P]> : never
}

type VueThis<P, D, C, M> = ThisType<VueProps<P> & D & VueComputed<C> & M>

type VueOptions<P, D, C, M> = {
  props: P;
  data: (this: VueProps<P>) => D;
  computed: C & VueThis<P, D, C, M>;
  methods: M & VueThis<P, D, C, M>
}

declare function VueBasicProps<P, D, C, M>(options: VueOptions<P, D, C, M>): unknown

自言自语

学到的点


上述方案比较偏『正向推导』,即,顺着形态,描述的是判断过程,偏过程化

https://github.com/type-challenges/type-challenges/issues/215 的解决方案基于类型推断,模式匹配,更偏描述式,更易于理解维护(推荐)

直接描述数据结构,然后 infer 一下子提取到最终类型

Solution by zhaoyao91 #20446

declare function VueBasicProps<
  // Base
  Props, Data, Computed, Methods,
  // Helper
  _ThisProps = {
    [P in keyof Props]: Props[P] extends ReturnType<
    /**/<Help extends ((() => infer T) | (abstract new (...args: any) => infer T & object))>() =>
    /*  */{ type?: Help | Help[] } | Help | Help[]
    > ? unknown extends T
        ? any
        : T
      : never
  },
  _ThisComputed = {
    [P in keyof Computed]: Computed[P] extends (...args: any) => infer R
      ? R
      : never
  },
>(options: {
  props?: Props,
  data?: (this: _ThisProps) => Data,
  computed?: Computed & ThisType<Data & _ThisComputed & _ThisProps>
  methods?: Methods & ThisType<Data & Methods & _ThisComputed & _ThisProps>,
}): any

Solution by lvjiaxuan #19188

type InferComputed<C> = C extends Record<string, (...args: any[]) => any>
  ? { [S in keyof C]: ReturnType<C[S]> }
  : never

type Prop<T> = PropType<T> | { type?: PropType<T> }

type PropType<T> = PropConstructor<T> | PropConstructor<T>[]

type PropConstructor<T> = | { new (...args: any[]): T & object } | { (): T }

type InferProps<P> = P extends Prop<infer T>
  ? unknown extends T
    ? any
    : T
  : any

type Props<T = any> = {
  [S in keyof T]: InferProps<T[S]>
}

declare function VueBasicProps<P, D, C, M>(
  options: {
    props: P,
    data(this: Props<P>): D,
    computed: C,
    methods: M
  } & ThisType<D & M & InferComputed<C> & Props<P>>
): any

Solution by milletlovemouse #18575

type GetRes<T> = T extends (...args: any[]) => infer R ? R : T extends abstract new (...args: any[]) => infer R ? R : any
declare function VueBasicProps<Props, Data, Computed, Methods, _Computed = {
  [key in keyof Computed]: GetRes<Computed[key]>
}, _Props = {
  [key in keyof Props]: Props[key] extends { type: infer T } ? T extends any[] ? GetRes<T[number]> : GetRes<T> : GetRes<Props[key]>
}>(options: {
  props: Props & ThisType<void>,
  data: (this: _Props) => Data,
  computed: Computed
  methods: Methods
} & ThisType<_Props & Data & _Computed & Methods>): any

Solution by BulatDashiev #16894

// your answers
type GetRes<T> = T extends () => infer R ? R : T extends abstract new(...args: any) => any ? InstanceType<T> : never
type GetComputed<T> = {
  [p in keyof T]: GetRes<T[p]>
}
type Props<T> = {
  [p in keyof T]: T[p] extends { type: infer R } ? R extends any[] ? GetRes<R[number]> 
                                                                   : GetRes<R> 
                                                 : keyof T[p] extends never ? any 
                                                                   : InstanceType<T[p]>
}
declare function VueBasicProps<P, D, C, M>(options: {
  props: P
  data(this: Props<P>): D & ThisType<P>
  computed: C & ThisType<D & C>
  methods: M & ThisType<D & GetComputed<C> & M & Props<P>>
}): any

Solution by Stan-BK #16432

// your answers

type Constructor = new (...args: any) => any

type PropsValue = Constructor | { type?: Constructor | Constructor[] }

type ConstructorMap<T> = T extends undefined
  ? any
  : T extends StringConstructor
    ? string
    : T extends NumberConstructor
      ? number
      : T extends RegExpConstructor
        ? RegExp
        : T extends BooleanConstructor
          ? boolean
          : T extends Constructor[]
            ? ConstructorMap<T[number]>
            : T extends { prototype: infer P }
              ? P
              : any

type PropsContext<T> = {
  [K in keyof T]: T[K] extends { type: infer R }
    ? ConstructorMap<R>
    : ConstructorMap<T[K]>
}

type OptionsType<Props extends Record<string, PropsValue>, Data, Computed, Methods> = {
  props?: Props,
  data?: (this: PropsContext<Props>) => Data;
  computed?: Computed & ThisType<Data & {
    [P in keyof Computed]: Computed[P] extends (...args: any) => infer R
      ? R
      : never
  } & PropsContext<Props>>;
  methods?: Methods & ThisType<PropsContext<Props> & Data & {
    [P in keyof Computed]: Computed[P] extends (...args: any) => infer R
      ? R
      : never
  } & Methods>;
}

declare function VueBasicProps<Props extends Record<string, PropsValue>, Data, Computed, Methods>(options: OptionsType<Props, Data, Computed, Methods>): any

Solution by humandetail #16422

// your answers
type ToResultTypes<
    T extends object> =
    {[J in keyof T] : T[J] extends (...args:any[])=>infer R ? R : T[J]} ;

    type ResultType<
    T> =
    T extends (...args:any[])=>infer R
    ? R
    : T extends new (...args:any[])=>infer S
    ? S
    : any ;  

type PropsTypes<
    P> =
    {[J in keyof P] 
    : P[J] extends {type: infer R} 
    ? R extends readonly any[]
    ? ResultType<R[number]>
    : ResultType<R>
    : ResultType<P[J]> }  ;

type T<
  P extends object,  
  C extends object,
  M extends object,
  S extends object >=
  {
    props:P,
    data(this:PropsTypes<P>):S,
    computed: C&ThisType<S>,
    methods: M&ThisType<PropsTypes<P>&M&ToResultTypes<C>>,
  } ;

declare function VueBasicProps<
  P extends object,
  S extends object,
  C extends object,
  M extends object
  >(options: T<P,S,C,M>): unknown ;

Solution by justBadProgrammer #15818

// your answers
type getType<T> = T extends String | Number | Boolean ? T extends { valueOf(): infer R } ? R : T : T

type OptionsByProps<P> = {
  [K in keyof P]:
  P[K] extends new (...args: any[]) => infer R ? R :
  P[K] extends { type: new (...args: any[]) => infer R } ? getType<R> :
  P[K] extends { type: (new (...args: any[]) => infer R)[] } ? getType<R> :
  P[K] extends {} ? any :
  P[K]
}

type OptionsByComputed<C> = {
  [K in keyof C]:
  C[K] extends (...args: any[]) => infer R ? R :
  never
}

type getOptions<D, P, C, M> = D & OptionsByProps<P> & M & OptionsByComputed<C>

type Options1<D, P, C, M> = {
  props: P
  data: (this: OptionsByProps<P>) => D & ThisType<getOptions<D, P, C, M>>
  computed: C & ThisType<getOptions<D, P, C, M>>
  methods: M & ThisType<getOptions<D, P, C, M>>
}

declare function VueBasicProps<D, P, C, M>(options: Options1<D, P, C, M>): any

Solution by fat-like-two #14878

// your answers
type getType<T> = T extends String | Number | Boolean ? T extends { valueOf(): infer R } ? R : T : T

type OptionsByProps<P> = {
  [K in keyof P]:
  P[K] extends new (...args: any[]) => infer R ? R :
  P[K] extends { type: new (...args: any[]) => infer R } ? getType<R> :
  P[K] extends { type: (new (...args: any[]) => infer R)[] } ? getType<R> :
  P[K] extends {} ? any :
  P[K]
}

type OptionsByComputed<C> = {
  [K in keyof C]:
  C[K] extends (...args: any[]) => infer R ? R :
  never
}

type getOptions<D, P, C, M> = D & OptionsByProps<P> & M & OptionsByComputed<C>

type Options1<D, P, C, M> = {
  props: P
  data: (this: OptionsByProps<P>) => D & ThisType<getOptions<D, P, C, M>>
  computed: C & ThisType<getOptions<D, P, C, M>>
  methods: M & ThisType<getOptions<D, P, C, M>>
}

declare function VueBasicProps<D, P, C, M>(options: Options1<D, P, C, M>): any

Solution by fat-like-two #14877

// your answers
type getType<T> = T extends String | Number | Boolean ? T extends { valueOf(): infer R } ? R : T : T

type OptionsByProps<P> = {
  [K in keyof P]:
  P[K] extends new (...args: any[]) => infer R ? R :
  P[K] extends { type: new (...args: any[]) => infer R } ? getType<R> :
  P[K] extends { type: (new (...args: any[]) => infer R)[] } ? getType<R> :
  P[K] extends {} ? any :
  P[K]
}

type OptionsByComputed<C> = {
  [K in keyof C]:
  C[K] extends (...args: any[]) => infer R ? R :
  never
}

type getOptions<D, P, C, M> = D & OptionsByProps<P> & M & OptionsByComputed<C>

type Options1<D, P, C, M> = {
  props: P
  data: (this: OptionsByProps<P>) => D & ThisType<getOptions<D, P, C, M>>
  computed: C & ThisType<getOptions<D, P, C, M>>
  methods: M & ThisType<getOptions<D, P, C, M>>
}

declare function VueBasicProps<D, P, C, M>(options: Options1<D, P, C, M>): any

Solution by fat-like-two #14876

type Constructor = new (...args: any) => any;

type ConstructorMap<T> =
  T extends undefined
  ? any
  : T extends StringConstructor
  ? string
  : T extends BooleanConstructor
  ? boolean
  : T extends NumberConstructor
  ? number
  : T extends RegExpConstructor
  ? RegExp
  : T extends Constructor[]
  ?  ConstructorMap<T[number]>
  : T extends {
    prototype: infer C
  }
  ? C
  : any

declare function VueBasicProps<
  Props extends Record<string, Constructor | { type?: Constructor | Constructor[] }>,
  Data,
  Computed,
  Methods
>(options: {
  props: Props & ThisType<null>,
  data (this: {
    [key in keyof Props]: Props[key] extends { type: infer P } ? ConstructorMap<P> : ConstructorMap<Props[key]>
  }): Data,
  computed: Computed,
  methods: Methods,
} & ThisType<{
  [key in keyof Props]: Props[key] extends { type: infer P } ? ConstructorMap<P> : ConstructorMap<Props[key]>
} & Data & {
  [key in keyof Computed]: Computed[key] extends () => infer Result ? Result : never
} & Methods extends infer Result ? {
  [key in keyof Result]: Result[key]
} : never>): any

Solution by cool-zero #14181


// The `computed` section needs to know about the inferred Props as it is an object
// where `this` is bound to those props.
type Computed<Props> = {
  [key: string]: (this: Props) => any
}

// We need a helper which converts the function-typed values in the `computed` section to 
// their actual types, which will be the return types of these functions.
//   We could certainly improve this and disallow non-function valued items in the object.
type Compute<T extends object> = {
  [P in keyof T]: 
    T[P] extends (...args: any[]) => infer Return
      ? Return
      : never
}

// Methods get `this` bound to an interface which contains everything specified before -
// so the props, the 'computed' `computed` section and all the methods themselves!
type MethodsContext<Props, Computed extends object> = 
  Props 
  &
  Compute<Computed>
  &
  Methods<Props, Computed>

// Methods is pretty simple - the complexity is in the interface that `this` implements
type Methods<Props, Computed extends object> = {
  [key: string]: (this: MethodsContext<Props, Computed>, ...args: any[]) => any
}

type Options<Props, Computed extends object> = {
  data: (this: never) => Props,
  computed: Computed,
  methods: Methods<Props, Computed>
}

declare function SimpleVue<
  Props, 
  ComputedWithProps extends Computed<Props>
>(
  options: Options<Props, ComputedWithProps>
): any

Solution by its-lee #13769

type PropsMap<T> = T extends (...args: any[]) => infer R1
  ? R1
  : T extends new (...args: any[]) => infer R2
  ? R2
  : never

type GetProps<P> = {
  [key in keyof P]: P[key] extends { type: infer R }
    ? R extends (infer P)[]
      ? PropsMap<P>
      : PropsMap<R>
    : Equal<P[key], {}> extends true
    ? any
    : PropsMap<P[key]>
}

type GetComputed<C> = {
  [key in keyof C]: ReturnType<
    C[key] extends (...args: any) => any ? C[key] : never
  >
}

declare function VueBasicProps<P, D, C, M>(options: {
  props: P
  data: (this: GetProps<P>) => D
  computed: C & ThisType<D>
  methods: M & ThisType<M & D & GetComputed<C> & GetProps<P>>
}): any

Solution by yukinotech #12897

type GetComputed<TComputed> = {
  [key in keyof TComputed]: TComputed[key] extends () => infer Result
    ? Result
    : never;
};

type Union<TValue> = TValue extends Array<unknown> ? TValue[number] : TValue;

type MyReturnType<TFunction> = TFunction extends () => infer Result
  ? Result
  : TFunction extends new (...params: any[]) => infer Result
  ? Result
  : any;

type InferProps<TProps> = {
  [key in keyof TProps]: MyReturnType<
    Union<TProps[key] extends { type: infer Type } ? Type : TProps[key]>
  >;
};

type Options<TProps, TData, TComputed, TMethods> = {
  props: TProps;
  data: (this: InferProps<TProps>) => TData;
  computed: TComputed & ThisType<TData>;
  methods: TMethods &
    ThisType<GetComputed<TComputed> & TMethods & InferProps<TProps>>;
};

declare function VueBasicProps<TProps, TData, TComputed, TMethods>(
  options: Options<TProps, TData, TComputed, TMethods>
): unknown;

Solution by michaltarasiuk #12600