00006-hard-simple-vue

Back

// 你的答案

declare function SimpleVue<D,C,M>(options: {
  data: (this: null) => D,
  // computed中的this可以指向data,并且还可以指向计算属性,准确的说是指向计算属性的返回值 this.fullname.firstname
  computed: C & ThisType<D & getComputedReturn<C>>,
  methods: M & ThisType<D & getComputedReturn<C> & M>
}): any

// 获取到计算属性的返回值类型
type getComputedReturn<T> = {
  [key in keyof T]: T[key] extends (...args: any[]) => infer R ? R : never 
}

Solution by chenjieya #33455

type Computed<T> = {
  [P in keyof T]: T[P] extends () => infer V ? V : never
}
type Options<D, C, M> = {
  data: (this: {}) => D,
  computed: ThisType<D & Computed<C> & M> & C,
  methods: ThisType<D & Computed<C> & M> & M
}

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

Solution by machine-solution #32961

type AmendComputed<T> = {
  [P in keyof T]: T[P] extends (...arg: any[]) => infer R ? R : T[P];
};
type Options<VData, VComputed, VMethod> = {
  data: (this: void) => VData;
  computed: VComputed & ThisType<VData & AmendComputed<VComputed>>;
  methods: VMethod & ThisType<VData & AmendComputed<VComputed> & VMethod>;
};
declare function SimpleVue<VData, VComputed, VMethod>(
  options: Options<VData, VComputed, VMethod>
): unknown;

Solution by Vampirelee #32598

type ID = object | (() => object);
type IC = Record<string, Function>;
type IM = Record<string, Function>;

type Mixed<
  D extends ID,
  C extends IC,
  M extends IM,
> = &
  (D extends () => object ? ReturnType<D> : D) &
  { readonly [key in keyof C]: C[key] extends () => any ? ReturnType<C[key]> : never } &
  M;

type Args<
  D extends ID,
  C extends IC,
  M extends IM,
> = {
  data: D,
  computed: C,
  methods: M,
}

declare function SimpleVue<
  D extends ID,
  C extends IC,
  M extends IM,
>(value: Args<D, C, M> & ThisType<Mixed<D, C, M>>): Mixed<D, C, M>;

const instance = SimpleVue({
  data() {
    return {
      firstname: 'Type',
      lastname: 'Challenges',
      amount: 10,
    }
  },
  computed: {
    fullname() {
      this.lastname = '1'
      return this.firstname + ' ' + this.lastname
    }
  },
  methods: {
    hi() {
      // Test internal this types
      this.firstname = 'test'
      alert(this.fullname.toLowerCase())
    },
    hi2(a: number) {
      return a.toString()
    }
  }
})

// Test some output types
const a = instance.amount;
const f = instance.fullname

const hi = instance.hi
const hi2 = instance.hi2

Solution by alexbidenko #32560

// your answers

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

declare function SimpleVue<D, C, M>(
  options: {
    data: () => D,
    computed: C,
    methods: M,
  } & ThisType<D & M & GetComputed<C>>
): any

const vue = SimpleVue({
  data() {
    return {
      firstname: 'Type',
      lastname: 'Challenges',
      amount: 10,
      symbol: Symbol('fff')
    }
  },
  computed: {
    fullname() {
      return this.firstname + ' ' + this.lastname
    },
    amountPlusOne() {
      let amount = this.plusOne()
      return amount
    },
    fullnameAsNickname() {
      return Symbol(this.fullname)
    }
  },
  methods: {
    plusOne() {
      return this.amount + 1
    },
    hi() {
      alert(this.fullname.toLowerCase())
    },
    alert() {
      this.hi()
    }
  }
})

// { [S in keyof C]: (...args: any[]) => any }
// like
// Record<string, (...args: any[]) => any>


Solution by laqudee #32356

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

Solution by vangie #32217

// your answers
type ComputedValue<Computed> = {
  [Properties in keyof Computed]: Computed[Properties] extends (...args: any[]) => infer R ? R : never
}

type VueOptions<Data, Computed, Method> = {
  data: (this: void) => Data
  computed: Computed & ThisType<Data>
  methods: Method & ThisType<Data & Method & ComputedValue<Computed>>
}

declare function SimpleVue<Data, Computed, Method>(options: VueOptions<Data, Computed, Method>): any

Solution by trinhvinhtruong96 #32027

 type ComputedValueType<C> = {
        [P in keyof C as C[P] extends () => any
          ? P
          : never]: C[P] extends () => infer R ? R : never;
      };
    declare function SimpleVue<Data,Computed,Methods>(options: {
      data : (this:void) => Data & ThisType<Data>,
      computed: Computed & ThisType<Computed & Data>,
      methods: Methods & ThisType<ComputedValueType<Computed> & Methods & Data>
    }): Data & Computed & Methods

Solution by chliguiy #31130

type OptionsType<C,D,M> = {
    data:(this:unknown)=> C,
    computed:D & ThisType<C>, 
    methods:M & ThisType<C & {[Key in keyof D] : D[Key] extends (...args:any[])=>infer V ? V : never} & M>,
} 

declare function SimpleVue<C,D,M>(options: OptionsType<C,D,M>): any

Solution by idebbarh #30308

declare function SimpleVue<Data, Computed extends Record<string, (this: Data) => any>, Methods extends Record<string, () => any>>(options: {
  data: (this: undefined) => Data
  computed: Computed,
  methods: Methods & { [key in keyof Methods]: (this: Data & { [key in keyof Computed]: ReturnType<Computed[key]> } & Methods) => any } 
}): any

Solution without ThisType

Solution by shokhboz-abdullaev #29902

declare function SimpleVue<
  D extends Record<string, unknown>,
  C extends Record<string, unknown>,
  M extends Record<string, () => void>,
>(options: {
  data?: (this: never) => D;
  computed?: {
    [K in keyof C]: (this: D) => C[K];
  };
  methods?: M & ThisType<M & D & C>;
}): any;

Solution by nia3y #29200

type GetComputed<Computed> = Computed extends Record<string, (...args: unknown[]) => unknown> 
  ? { [Key in keyof Computed]: ReturnType<Computed[Key]> } 
  : never


type OptionsType<TData, TComputed, TMethods> = {
  data: (this: null) => TData
  computed: TComputed
  methods: TMethods
} & ThisType<TData & GetComputed<TComputed> & TMethods>

declare function SimpleVue<TData, TComputed, TMethods>(options: OptionsType<TData, TComputed, TMethods>): any

Solution by aybykovskii #28809

6 - Simple Vue

Don't even try. Just watch this one. So far as anyone can tell, there's no way to accomplish this one without use of a special (and extremely rare) type that the TypeScript compiler has special knowledge of.

🎥 Video Explanation

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

Simple Vue

🔢 Code

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

SimpleVue({
  data() {
    // @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.amount)
      alert(this.fullname.toLowerCase())
      alert(this.getRandom())
    },
    test() {
      const fullname = this.fullname

      type C1 = Expect<Equal<typeof fullname, string>>
    },
  },
});

// ============= Your Code Here =============
declare function SimpleVue<
  Data,
  Computed extends
    Record<PropertyKey, (...args: unknown[]) => unknown>,
  Methods
>(
  options: {
    data: (this: void) => Data;
    computed: Computed & ThisType<Data>;
    methods:
      Methods
      & ThisType<
          & Data
          & 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 #26113

interface Options<D, C extends Record<PropertyKey, () => unknown>, M> {
  data: (this: never) => D,
  computed: C & ThisType<D>,
  methods: M & ThisType<D & {[Key in keyof C]: ReturnType<C[Key]>} & M>
};

declare function SimpleVue<D, C extends Record<PropertyKey, () => unknown>, M>(options: Options<D, C, M>): unknown

Solution by bryzZz #25931

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 #25872

type Computed<C extends Record<PropertyKey, () => any>> = {
  [CK in keyof C]: ReturnType<C[CK]>;
};

type SimpleVueOptions<
  D extends Record<PropertyKey, any>,
  C extends Record<PropertyKey, () => any>,
  M extends Record<PropertyKey, () => any>
> = {
  data: () => D;
  computed: C & ThisType<D>;
  methods: M & ThisType<D & Computed<C> & M>;
} & ThisType<null>;

declare function SimpleVue<
  D extends Record<PropertyKey, any>,
  C extends Record<PropertyKey, () => any>,
  M extends Record<PropertyKey, () => any>
>(options: SimpleVueOptions<D, C, M>): any;

Solution by Creo-KR #25209

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

Solution by ickynavigator #25031

// your answers
// your answers
declare function SimpleVue<
    T extends { [x: PropertyKey]: any },
    U extends { [x: string]: () => any },
    V extends { [x: string]: () => any }
>(
    options: {
        data: () => T
        computed: U & ThisType<T>
        methods: V &
            ThisType<
                T &
                    V & {
                        [P in keyof U]: U[P] extends () => infer R ? R : never
                    }
            >
    } & ThisType<never>
): any

Solution by studymachiney #24950

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

Solution by E-uler #24573

// 你的答案
declare function SimpleVue<
  D extends Record<string,unknown>,
  C extends Record<string,unknown>,
  M extends Record<string,unknown>
>(options: {
  data:(this:never)=>D
 computed:{
    [key in keyof C]:(this:D)=>C[key]
  }
  methods:{
    [key in keyof M]:(this:D&C&{[K in keyof M]:()=>M[K]})=>M[key]
  }
}): any

Solution by walker-hzx #24279

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

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

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

Solution by drylint #23161

// 你的答案
type FunctionReturn<T> = T extends (...args: any) => infer R ? R : T;

declare function SimpleVue<
D,
M,
C
>(options: {
  data: D,
  methods: M,
  computed: C,
} & ThisType<
FunctionReturn<D> & M & {[key in keyof C]: FunctionReturn<C[key]>} & {  
  data: D,
  methods: M,
  computed: C
  }> ): any

Solution by 437204933 #21979

// 你的答案
// 0 一开始只添加了 D,可是写methods时这货竟然需要 computed的类型,
// 1 然后又发现这货又用this 调用自己的函数, 于是又加了M才把红线消除完毕.
declare function SimpleVue<D, C, M>(options: TestOption<D, C, M>): TestOption<D, C, M>
// 2 起初看不懂这题到底要干啥,后来看着看着发现这题是想解决this问题
type TestOption<D, C, M> = {
    data?: D,
    // 3 如果 D是函数类型, 那么 computed 的 this 应该是它本身和 D的返回值或D本身
    computed?: C & ThisType<D extends () => infer R ? R : D>,
    // 4 methods里的this 同样也是 M本身 
    // 5 和 D... 
    // 6 和 C里面的各种函数 改为 函数名:函数返回值类型的 新类型
    methods?: M & ThisType<
        (D extends () => infer R ? R : D)
        & { [K in keyof C]: C[K] extends (...argv: any) => infer R ? R : C[K] }
        & M // 7 写到这里,发现错误信息都没了.
    >,
}

Solution by goddnsgit #21834

// your answers
type Options<D,C,M> = {
  data(this:never):D
  computed:C & ThisType<D>,
  methods:{
    [K in keyof M]:M[K]
  } & ThisType<D & M & {
    [K1 in keyof C] : C[K1] extends () => any ? ReturnType<C[K1]> : C[K1]
  }>
}
declare function SimpleVue<D,C,M>(options: Options<D,C,M>): any

Solution by YqxLzx #21315

type RecordFunction = Record<string,Function>;
type TransitionComputed<T extends RecordFunction> = {
  [P in keyof T]:T[P] extends ()=>infer A ? A :never
};
type Vue<
Data extends Record<string,any>,
Computed extends RecordFunction,
Methods  extends RecordFunction,
>  = {
  data?():Data;
  computed?: Computed & ThisType<Methods & TransitionComputed<Computed> & Data>,
  methods?: Methods & ThisType<Methods & TransitionComputed<Computed> & Data>,
}

declare function SimpleVue<
Data extends Record<string,any>,
Computed extends RecordFunction,
Methods  extends RecordFunction,
>(options: Vue<Data,Computed,Methods>): any

Solution by so11y #20969

type VueOptions<D, C, M> = {
  data: (this: {}) => D,
  computed: C & ThisType<D>,
  methods: M & ThisType<D & M & ComputedValues<C>>
}

interface MyVue {
  new <D, C, M>(options: VueOptions<D, C, M>): any
}

declare const MyVue: MyVue

const instance1 = new MyVue({
  data() {
    return {
      firstname: 'Type',
      lastname: 'Challenges',
      amount: 10,
    }
  },
  computed: {
    fullname() {
      return this.firstname + ' ' + this.lastname
    }
  },
  methods: {
    hi() {
      alert(this.fullname.toLowerCase())
    }
  }
})

Solution by m7yue #20367

// your answers
type DataFunction<D> = (this:never) => D;
type Computed<D, C> = { [K in keyof C]: (this: D) => C[K] };
type Methods<D, C, M> = { [K in keyof M]: (this:D & C & M) => M[K] };
type Options<D, C, M> =  {
  data: DataFunction<D>,
  computed: Computed<D, C>,
  methods:M & ThisType<D & M & C>
};

declare function SimpleVue<D, C, M>(options: Options<D, C, M>): D & C & M;

Solution by YqxLzx #20315

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

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

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

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

第一次做到这里,确实打脑壳了,向历史答案们致敬…

学到了:

Solution by zhaoyao91 #20089

type DataOption = object | ((this: void) => object)

type TD<T> = T extends () => infer R ? R : T
type TC<T> = {
  [P in keyof T]: T[P] extends () => infer R ? R : T[P]
}

type VueOptions<
  D extends DataOption,
  C,
  M,
> = {
  data?: D
  computed?: C
  methods?: M & {
    [P in string]: (this: TD<D> & TC<C> & VueOptions<D, C, M>['methods']) => void
  }
}

declare function SimpleVue<D extends DataOption, C, M>(options: VueOptions<D, C, M>): VueOptions<D, C, M>

虽然能够通过测试用例, 但是在 computed 中的方法, this 的类型还是 any,不能被正确推导。

Solution by pengzhanbo #19992

// your answers
type OptionData = (() => any) | object
type OptionCopmuted = Record<string, Function>
type OptionMethods = Record<string, Function>

type TransformComputed<Computed extends OptionCopmuted> = {
  [K in keyof Computed]: Computed[K] extends () => infer R ? R : Computed[K]
}

type TransformData<Data extends OptionData> = Data extends (() => any)
? ReturnType<Data>
: Data

type VueOptions<Data extends OptionData, Computed extends OptionCopmuted, Methods extends OptionMethods> = {
  data?: Data
  computed?: Computed & ThisType<TransformData<Data> & TransformComputed<Computed> & Methods>
  methods?: Methods & ThisType<TransformData<Data> & TransformComputed<Computed> & Methods>
}

declare function SimpleVue<
  Data extends OptionData,
  Computed extends OptionCopmuted,
  Methods extends OptionMethods,
  Options extends VueOptions<Data, Computed, Methods>

>(options: VueOptions<Data, Computed, Methods>): Options

Solution by FrontendKevinLi #19746