Reputation: 4622
I have the following class
class StoreBuilder<TStore> {
private _map: Map<string, Function>
private _selectors: Map<string, Function>
constructor(){
this._map = new Map<string, Function>()
this._selectors = new Map<string, Function>()
}
add<TArg>(kind: string, code: (s:TStore, x:TArg)=>void){
if(!this._map.has(kind)){
const func = (store: TStore, a0: TArg)=>{
code(store, a0)
}
this._map.set(kind, func)
}
if(!this._selectors.has(kind)){
const func = (arg: TArg)=>{
return {type: kind, payload: arg}
}
this._selectors.set(kind, func)
}
}
build(){
const reducer = (store: TStore, action: {type: string, payload: any})=>{
if(this._map.has(action.type)){
try {
const code = this._map.get(action.type)
code(store, action.payload)
} catch (err) {
console.error(err)
}
}
}
const commands = new Object()
for(const [name, func] of this._selectors){
commands[name] = func
}
const ret = { commands, reducer}
return ret
}
}
this is how I'm using it
type mystoretype ={
coins: number
}
const coiner = new StoreBuilder<mystoretype>()
coiner.add<number>('addCoin', (s,x)=>{
s.coins += x
})
coiner.add<number>('takeCoin', (s,x)=>{
if(s.coins>x){
s.coins-= x
}
})
const {commands, reducer} = coiner.build()
but when I do this, works in javascript but gives error in typescript:
const cmd1 = commands.addCoin(23) //Property 'addCoin' does not exist on type 'Object'.ts(2339)
const cmd2 = commands.takeCoin(34) //Property 'takeCoin' does not exist on type 'Object'.ts(2339)
Upvotes: 2
Views: 549
Reputation: 2020
I didn't know the answer myself, but I was intrigued to find a solution.
I don't think you can use your current syntax to achieve this result.
However, if you'd be open to modifying the way the builder is created to a Fluent Interface using Method chaining, then the following might be an option.
Note: I've simplified your example and removed the actual implementation to increase readability of the core idea. Hopefully this helps to get the idea accross.
interface Builder<TStore, TType = {}> {
add<TName extends string, TArg>(key: TName, action: (store: TStore, param: TArg) => void): Builder<TStore, TType & { [key in TName]: (param: TArg) => void }>;
build: () => TType;
}
// Example - Creation and usage
type mystoretype ={
coins: number
};
const builder = {} as Builder<mystoretype>; // Using your existing implementation
const instance = builder
.add("addCoin", (s, x: number) => { s.coins += x })
.add("takeCoin", (s, x: string) => {})
.build();
instance.addCoin(12);
instance.takeCoin("Abc");
// Example of errors
instance.addCoin("Abc"); // Error: as a string is not a number
instance.takeCoin(12); // Error: as a number is not a string
Upvotes: 3