// your answers
Solution by liuyouqing111 #33791
declare function defineStore<S, G, A>(store: {
id: string;
state: () => Readonly<S>;
getters?: G & ThisType<Readonly<S> & GetGetters<G>>;
actions?: A & ThisType<S & A>;
}): Readonly<S> & GetGetters<G> & A;
type GetGetters<T> = {
[P in keyof T]: T[P] extends (...args: any[]) => infer R ? R : never;
};
Solution by BitAstray #33639
declare function defineStore<S, G, A>(
store: PiniaOptions<S, G, A>
): A & S & GetGetter<G>;
type GetGetter<T> = {
[P in keyof T]: T[P] extends () => infer R ? R : T[P]
}
type PiniaOptions<S, G, A> = {
id: string;
state: () => S;
getters: G & ThisType<Readonly<S> & GetGetter<G>>;
actions: A & ThisType<S & Readonly<GetGetter<G>> & A>;
};
Solution by Vampirelee #32629
type FunToProp<G> = {
[K in keyof G]: G[K] extends (...args: any[]) => any
? ReturnType<G[K]>
: never;
};
declare function defineStore<S, G, A>(store: {
id: string;
state: () => S;
getters: G & ThisType<Readonly<S> & FunToProp<G>>;
actions: A & ThisType<S & A>;
}): A & S & FunToProp<G>;
Solution by vangie #32336
type Flatten<G extends Record<string, Function>> = {
readonly [key in keyof G]: G[key] extends () => unknown ? ReturnType<G[key]> : never
}
declare function defineStore<S, G extends Record<string, Function>, A extends Record<string, Function>>(store: {
id: string,
state: () => S,
getters: G & ThisType<Readonly<S> & Flatten<G>>,
actions: A & ThisType<S & Flatten<G> & A>
}): A & S & Flatten<G>
Solution by Ayc0 #31451
type Resolve<O> = O extends { [P: string]: (...args: any[]) => any }
? {
[P in keyof O]: ReturnType<O[P]>
}
: never
declare function defineStore<D, G, A>(store: {
id: string
state: () => D
getters?: G & ThisType<Readonly<D & Resolve<G>>>
actions?: A & ThisType<D & A>
}): D & Resolve<G> & A
Solution by Chan-Yuxi #30692
type BaseObject = Record<string, unknown>
// type BaseGetters = Record<string, (...args: any[]) => any>
type BaseGetters = Record<string, Function>
// type BaseActions = Record<string, (...args: any[]) => any>
type BaseActions = Record<string, Function>
type GetComputedGetters<Getters extends BaseGetters> = {
[Key in keyof Getters]: Getters[Key] extends (...args: unknown[]) => infer RT ? RT : never
}
type Store<
State extends BaseObject,
Getters extends BaseGetters,
Actions extends BaseActions
> = {
id: string
state: () => State
getters: Getters & ThisType<Readonly<State> & GetComputedGetters<Getters>>
actions: Actions & ThisType<State & Actions & Readonly<Getters>>
}
declare function defineStore<
State extends BaseObject,
Getters extends BaseGetters,
Actions extends BaseActions
>(store: Store<State, Getters, Actions>): State & GetComputedGetters<Getters> & Actions
Solution by aybykovskii #28848
type Getter<T> = {
readonly [key in keyof T]: T[key] extends (...args: any) => any
? ReturnType<T[key]>
: never;
};
declare function defineStore<
S extends Record<string, any>,
G extends Record<string, Function>,
A extends Record<string, Function>,
>(store: {
id: string;
state: () => S;
getters?: G & ThisType<Readonly<S> & Getter<G>>;
actions?: A & ThisType<A & S & Getter<G>>;
}): S & Getter<G> & A;
Solution by disanrencheng #27879
type AnyObject = Record<string, unknown>;
type BaseGetters = Record<string, Function>;
type BaseActions = Record<string, (...args: any[]) => any>;
type ComputedGetters<G extends BaseGetters> = {
readonly [K in keyof G]: G[K] extends (...args: any[]) => any ? ReturnType<G[K]> : never;
}
interface StoreOptions<
State extends AnyObject,
Getters extends BaseGetters,
Actions extends BaseActions
> {
id: string;
state: () => State;
getters: Getters & ThisType<ComputedGetters<Getters> & Readonly<State>>,
actions: Actions & ThisType<Actions & State & ComputedGetters<Getters>>,
};
type Store<
State extends AnyObject,
Getters extends BaseGetters,
Actions extends BaseActions
> = {
init(): void;
reset(): true;
} & Actions & State & ComputedGetters<Getters>;
declare function defineStore<
State extends AnyObject,
Getters extends BaseGetters,
Actions extends BaseActions
>(store: StoreOptions<State, Getters, Actions>): Store<State, Getters, Actions>
Solution by viktor-sakhno-saritasa #27673
I haven't saw a solution where someone used generic constraints (i.e. extends ...
). which I think is really important for the real library.
Here is my solution:
type AnyObject = Record<string, unknown>
type BaseGetters = Record<string, Function> ;
type BaseActions = Record<string, (...args:any[]) => any>;
type ComputedGetters<G extends BaseGetters> = {
readonly [K in keyof G]: G[K] extends (...args:any[]) => any ? ReturnType<G[K]> : never;
};
interface StoreOptions<State extends AnyObject, Getters extends BaseGetters, Actions extends BaseActions> {
id: string;
state: () => State,
getters: Getters & ThisType<ComputedGetters<Getters> & Readonly<State>>,
actions: Actions & ThisType<Actions & State & ComputedGetters<Getters>>
}
type Store<
State extends AnyObject,
Getters extends BaseGetters,
Actions extends BaseActions
>= {
init(): void;
reset(): true;
} & Actions & State & ComputedGetters<Getters>;
declare function defineStore<
State extends AnyObject,
Getters extends BaseGetters,
Actions extends BaseActions
>(store: StoreOptions<State, Getters, Actions>): Store<State, Getters, Actions>;
Solution by Ayub-Begimkulov #27536
It's a great feeling to see a real-world use-case where we can put these skills into use. Pinia is a state management library, and in this challenge we write a type for its the top-level API.
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
const store = defineStore({
id: '',
state: () => ({
num: 0,
str: '',
}),
getters: {
stringifiedNum() {
// @ts-expect-error
this.num += 1
return this.num.toString()
},
parsedNum() {
return parseInt(this.stringifiedNum)
},
},
actions: {
init() {
this.reset()
this.increment()
},
increment(step = 1) {
this.num += step
},
reset() {
this.num = 0
// @ts-expect-error
this.parsedNum = 0
return true
},
setNum(value: number) {
this.num = value
},
},
})
// @ts-expect-error
store.nopeStateProp
// @ts-expect-error
store.nopeGetter
// @ts-expect-error
store.stringifiedNum()
store.init()
// @ts-expect-error
store.init(0)
store.increment()
store.increment(2)
// @ts-expect-error
store.setNum()
// @ts-expect-error
store.setNum('3')
store.setNum(3)
const r = store.reset()
type A1 = typeof store.num;
type B1 = number;
type C1 = Expect<Equal<A1, B1>>;
type A2 = typeof store.str;
type B2 = string;
type C2 = Expect<Equal<A2, B2>>;
type A3 = typeof store.stringifiedNum;
type B3 = string;
type C3 = Expect<Equal<A3, B3>>;
type A4 = typeof store.parsedNum;
type B4 = number;
type C4 = Expect<Equal<A4, B4>>;
type A5 = typeof r;
type B5 = true;
type C5 = Expect<Equal<A5, B5>>;
// ============= Your Code Here =============
type ObjectValueReturnType<T> = {
readonly [P in keyof T]:
T[P] extends (...args: any[]) => infer R
? R
: never
}
declare function defineStore<State, Getters, Actions> (
store: {
id: string
state: () => State
getters:
& Getters
& ThisType<
& Readonly<State>
& ObjectValueReturnType<Getters>
>,
actions:
& Actions
& ThisType<
& State
& ObjectValueReturnType<Getters>
& Actions
>
},
):
& State
& ObjectValueReturnType<Getters>
& Actions;
For more video solutions to other challenges: see the umbrella list! https://github.com/type-challenges/type-challenges/issues/21338
Solution by dimitropoulos #26115
type Getters<T extends { [x: string]: Function }> = { readonly [P in keyof T]: T[P] extends () => infer R ? R : any };
type Store<
_State extends { [x: string]: any },
_GetterFuncs extends { [x: string]: Function },
_Actions extends { [x: string]: Function }
> = {
id: string,
state: () => _State,
getters: _GetterFuncs & ThisType<Readonly<_State> & Getters<_GetterFuncs>>,
actions: _Actions & ThisType<_Actions & _State & Getters<_GetterFuncs>>
}
declare function defineStore<
_S extends { [x: string]: any },
_G extends { [x: string]: Function },
_A extends { [x: string]: Function }
>(store: Store<_S, _G, _A>): Readonly<_S> & _A & Getters<_G>;
Solution by E-uler #24960
type GettersReturnType<G> = {
readonly [K in keyof G]: G[K] extends (...args: never[]) => infer R ? R : never;
}
interface Store<S, G, A> {
id: string
state: () => S
getters?: G & ThisType<Readonly<S> & GettersReturnType<G> & A>
actions?: A & ThisType<S & GettersReturnType<G> & A>
}
declare function defineStore<S, G, A>(store: Store<S, G, A>): Readonly<S> & GettersReturnType<G> & A
Solution by TKBnice #23318
type Getters<G> = {
readonly [K in keyof G]: G[K] extends (...args: any[]) => infer R ? R : never
}
interface StoreOptions<S, G, A> {
id: string
state: () => S
getters: G & ThisType<Readonly<S> & Getters<G>>
actions: A & ThisType<S & Getters<G> & Readonly<A>>
}
declare function defineStore<S, G, A> (
storeOptions: StoreOptions<S, G, A>,
): S & Getters<G> & Readonly<A>
Solution by drylint #23214
type InferGetters<T> = T extends Record<string, (...args: any[]) => any> ? {
[K in keyof T]: ReturnType<T[K]>
}: never;
declare function defineStore<State, G, A>(store: {
id: string,
state: () => State
getters: G & ThisType<Readonly<State> & InferGetters<G> & A>,
actions: A & ThisType<State & Readonly<InferGetters<G>> & A>
}): State & InferGetters<G> & A
Solution by coderyoo1 #23024
// easily comparable function type
type Fn = (...args: any) => unknown;
// tranform object to contain all attributes and transform function types to their return types
type TypeOrReturnType<T> = {
[key in keyof T]: T[key] extends Fn ? ReturnType<T[key]> : T[key]
};
type StoreConfig<S, G, A> = {
id: string;
state: () => S;
getters: G & ThisType<TypeOrReturnType<G> & Readonly<S>>;
actions: A & ThisType<A & S & Readonly<TypeOrReturnType<G>>>;
};
declare function defineStore<S,G,A>(store: StoreConfig<S,G,A>): A & S & TypeOrReturnType<G>
Solution by Karamuto #22036
// ๆ็นไนฑใใใๆผๆผๅๅ็ๆ็ปๆ้่ฏฏๆถ้คๅฎไบใใใ
declare function defineStore<S, G, A>(store: StoreOptioon<S, G, A>)
: A & S & GETTERS<G> & StoreOptioon<S, G, A>
type GETTERS<G> = { [K in keyof G]: G[K] extends () => infer R ? R : never }
type StoreOptioon<S, G, A> = {
id: string
state?: () => S
getters?: G & ThisType<GETTERS<G> & Readonly<S>>
actions?: A & ThisType<S & A>
}
Solution by goddnsgit #21906
type StoreType = Record<string, any>;
type GettersType = Record<string, Function>;
type ActionsType = Record<string, Function>;
type TransformGetters<T> = {
[P in keyof T]: T[P] extends () => infer R ? R : never;
};
interface Pinia<
Store extends StoreType,
Getters extends GettersType,
Actions extends ActionsType
> {
id: string;
state: () => Store;
getters: Getters & ThisType<Readonly<Store> & TransformGetters<Getters>>;
actions: Actions & ThisType<Store & Actions>;
}
declare function defineStore<
Store extends StoreType,
Getters extends GettersType,
Actions extends ActionsType
>(
store: Pinia<Store, Getters, Actions>
): Store & Actions & TransformGetters<Getters>;
Solution by so11y #21233
type GetRes<T> = T extends (...args: any[]) => infer R ? R : never
declare function defineStore<State, Getters, Actions, _Getters = {
readonly [P in keyof Getters]: GetRes<Getters[P]>
}>(store: {
id: string,
state: (this: void) => State,
getters?: Getters & ThisType<_Getters & Readonly<State>>
actions?: Actions & ThisType<State & _Getters & Actions>
}): State & _Getters & Actions;
Solution by BulatDashiev #16942
// your answers
type StoreOptions<State, Getters, Actions> = {
id?: string;
state?: () => State;
getters?: Getters & ThisType<Readonly<State> & {
readonly [P in keyof Getters]: Getters[P] extends (...args: any) => infer R
? R
: never
}>;
actions?: Actions & ThisType<State & {
readonly [P in keyof Getters]: Getters[P] extends (...args: any) => infer R
? R
: never
} & Actions>;
}
declare function defineStore<State, Getters, Actions>(store: StoreOptions<State, Getters, Actions>): Readonly<State> & {
readonly [P in keyof Getters]: Getters[P] extends (...args: any) => infer R
? R
: never
} & Actions
Solution by humandetail #16480
// your answers
type Store<
I extends string,
S extends object,
G extends object,
A extends object> =
{
id: I,
state: ()=>S,
getters: G&ThisType<Readonly<S>&Map<G>>,
actions: A&ThisType<A&S&Readonly<G>>
} ;
declare function defineStore<
I extends string,
S extends object,
G extends object,
A extends object>(store: Store<I,S,G,A>): Readonly<S&Map<G>&A> ;
Solution by justBadProgrammer #15903
// We don't need this helper, but it's really nice to do this as people use nested
// state a lot in Pinia / Vuex / your favourite state management solution.
type DeepReadonly<T> = {
readonly [K in keyof T]:
T[K] extends object
? DeepReadonly<T[K]>
: T[K]
}
// For computed getters, map the values from functions to their return types,
// representing their cached values.
// There's an enhancement we should make here which is that if the getters list a
// function with at least one parameter, this is not a computed property, but a
// function that returns that function (or something like that - as obviously you
// can't cache something (fully) which accepts parameters).
type Compute<Getters extends object> = Readonly<{
[P in keyof Getters]: Getters[P] extends () => infer Result
? Result
: never
}>
// Getters get (deep) readonly access to the state, and all of the other (computed) getters
type Getters<State> = {
[P in keyof any]: (
this: DeepReadonly<State> & Compute<Getters<State>>,
...args: any[]
) => any
}
// Actions get non-readonly access to the state, and all of the other (computed) getters
type Actions<State> = {
[P in keyof any]: (
this: State & Compute<Getters<State>>,
...args: any[]
) => any
}
type Options<State extends object, GettersWithState, ActionsWithState> = {
id: string,
state: () => State,
getters: GettersWithState,
actions: ActionsWithState
}
type Store<State extends object, Getters extends object, Actions extends object> =
State
&
Compute<Getters>
&
Actions
// We infer the State, and then place constraints on the user-defined Getters &
// Actions based on the State, which leads to some great auto-complete & type-checking
// when calling defineStore.
declare function defineStore<
State extends object,
GettersWithState extends Getters<State>,
ActionsWithState extends Actions<State>
>(store: Options<State, GettersWithState, ActionsWithState>)
: Store<State, GettersWithState, ActionsWithState>
Solution by its-lee #13771
// your answers
declare function defineStore<State, Getters, Actions,
ThisGetters = {
readonly [K in keyof State]: State[K]
} & {
readonly [K in keyof Getters]: Getters[K] extends () => infer R ? R : Getters[K]
} extends infer Merge ? { [K in keyof Merge]: Merge[K] } : never,
ThisActions = State & Getters & Actions
>(store: {
id: string,
state: () => State,
getters: Getters & ThisType<ThisGetters>,
actions: Actions & ThisType<ThisActions>,
}): ThisGetters & Actions
Solution by liuxing95 #13056
// your answers
declare function defineStore<S, G, A>(store: {
id: string;
state: () => S;
getters: G &
ThisType<
Readonly<S> & { [K in keyof G]: G[K] extends () => infer R ? R : never }
>;
actions: A & ThisType<S & A>;
}): S & { [K in keyof G]: G[K] extends () => infer R ? R : never } & A;
Solution by SeptEarlyMorning #12136
declare function defineStore<State, Getters, Actions,
ThisGetters = {
readonly [K in keyof State]: State[K]
} & {
readonly [K in keyof Getters]: Getters[K] extends () => infer R ? R : Getters[K]
} extends infer Merge ? { [K in keyof Merge]: Merge[K] } : never,
ThisActions = State & Getters & Actions
>(store: {
id: string,
state: () => State
getters: Getters & ThisType<ThisGetters>
actions: Actions & ThisType<ThisActions>,
}): ThisGetters & Actions
Solution by teamchong #11824
declare function defineStore<S, G, A>(store: {
id: string
state: () => S
getters: G & ThisType<S & {[K in keyof G]: G[K] extends () => infer TReturn ? TReturn : G[K]}>
actions: A & ThisType<S &A & {[K in keyof G]: G[K] extends () => infer TReturn ? TReturn : G[K]} >
}): S & A & {[K in keyof G]: G[K] extends () => infer TReturn ? TReturn : G[K]}
Solution by wqs576222103 #11070
// ไธ็กฎๅฎ่ฟๆ ทๆฏไธๆฏๅฏน็ ๐ฅ
type UnionToIntersect<T> = (T extends T ? (x: T) => unknown : never) extends (x: infer P) => unknown ? P : never;
type Getter<T, K = keyof T> = UnionToIntersect<
K extends keyof T ?
{
[Key in K]: T[Key] extends () => infer ReturnType ? ReturnType : never
}
: never
>;
declare function defineStore<S, G, A>(store: {
id: string,
state(): S,
getters: G & ThisType<Getter<G> & S>,
actions: A & ThisType<A & S & Getter<G>>
}): S & Getter<G> & A;
Solution by XkSuperCool #10613
declare function defineStore<S,G extends Record<string,any>,A>(store: {
id: string,
state:() => S,
getters: G & ThisType<S & {
[key in keyof G]: (G[key] extends () => infer Rrturn ? Rrturn: G[key])
} >,
actions: A & ThisType<A & S & ({
[key in keyof G]: (G[key] extends () => infer Rrturn ? Rrturn: G[key])
})>
}): A & S & ({
[key in keyof G]: (G[key] extends () => infer Rrturn ? Rrturn: G[key])
})
Solution by EGyoung #8616
// your answers
type GetGettersType<T> = {
[P in keyof T]: T[P] extends (...args: unknown[]) => infer R ? R : never
}
declare function defineStore<D, G, A>(store: {
id: string
state: () => D
getters: G & ThisType<Readonly<D> & GetGettersType<G>>
actions: A & ThisType<D & Readonly<G> & A>
}): GetGettersType<G> & A & D
Solution by godlanbo #7578
// 1290 - Pinia
type GetThisWithoutState<Getters, Actions> = { readonly [K in keyof Getters]: Getters[K] extends (...args: never[]) => infer R ? R : never; } & Actions;
declare function defineStore< State, Getters, Actions, ThisWithoutState = GetThisWithoutState<Getters, Actions>
(store: { id: string; state: () => State; getters?: Getters & ThisType<Readonly
& ThisWithoutState>; actions?: Actions & ThisType<State & ThisWithoutState>; }): Readonly & ThisWithoutState;
Solution by Carefree-happy #7118