Reputation: 6867
I have these two methods which are almost similar:
private firstFunction () {
this.serviceOne.methodOne().subscribe(
res => {
return resultOne = res;
},
err => {}
);
}
private secondFunction () {
this.serviceTwo.methodTwo().subscribe(
res => {
return resultTwo = res;
},
err => {}
);
}
I want to write a generic function, like this:
genericFunction (service ,method , result ) {
service.method().subscribe(
res => {
return result = res;
},
err => {}
);
}
And consequently I want to get something like this working:
genericFunction (serviceOne , methodOne , resultOne );
genericFunction (serviceTwo , methodTwo , resultTwo );
Actually, I cannot find how to pass methodOne
and methodTwo
as params. Any sugestions?
Upvotes: 4
Views: 26655
Reputation: 249546
There are several issues in your code.
Firstly, you want to modify the field you pass in as a parameter (as suggested by result = res
. You can't pass in a reference to a field, but you can pass in the field name, and use indexing to change the field. keyof T
will allow you to pass in the field in a type safe way.
Secondly if you want to access a method on a service. Again we can do this passing in the method name, and we can constrain the service to have a method with the passed in method name, that returns an Observable
. The result of the Observable
can also be constrained to be of the same type of the field we are going to assign it to in order for the method to be fully type safe.
declare class Service1 {
method1() : Observable<number>
}
declare class Service2 {
method2() : Observable<string>
}
class MyClass {
resultOne!: number;
resultTwo!: string;
constructor() {
this.genericFunction(new Service1(), "method1", "resultOne");
this.genericFunction(new Service2(), "method2", "resultTwo");
this.genericFunction(new Service1(), "method1", "resultTwo"); // error resultTwo is a string, the method return Observable<number>
this.genericFunction(new Service2(), "method", "resultTwo"); // error method does not exit on Service2
this.genericFunction(new Service2(), "method2", "resultTwo2"); // error field does not exist on type
}
genericFunction<MethodKey extends string, ResultKey extends keyof MyClass>(service:Record<MethodKey, ()=> Observable<MyClass[ResultKey]>>, method:MethodKey, result: ResultKey){
service[method]().subscribe(
res => this[result] = res,
err => {}
);
}
}
Note We could have also passed in the function as a function not just as a name, but directly a typed function. The disadvantage of this is that we either have to use bind
to ensure the service method will still have the correct this
when it's called, or use an arrow function when calling (again to ensure the service method has the correct this
). This is error prone though, bind
results in an untyped function, so we can't check compatibility to the field, and someone might pass service.method
directly and no error would be reported until runtime:
class MyClass {
resultOne!: number;
resultTwo!: string;
constructor() {
var service1 = new Service1()
var service2 = new Service2()
this.genericFunction(()=> service1.method1(), "resultOne");
this.genericFunction(()=> service2.method2(), "resultTwo");
this.genericFunction(service2.method2, "resultTwo"); // no error, depending on the implementation of method2 it might or might not work
this.genericFunction(service2.method2.bind(service2), "resultOne"); // no error, the service call will work, but we store it in an incompatible variable
this.genericFunction(()=> service1.method1(), "resultTwo");// error resultTwo is a string, the method return Observable<number>
this.genericFunction(()=> service2.method2(), "resultTwo2");// // error field does not exist on type
}
genericFunction<MethodKey extends string, ResultKey extends keyof MyClass>(method:()=> Observable<MyClass[ResultKey]>, result: ResultKey){
method().subscribe(
res => this[result] = res,
err => {}
);
}
}
Upvotes: 1
Reputation: 2408
try by using the following code:
private firstFunction () {
let response= genericFunction(this.serviceOne.methodOne())
}
private secondFunction () {
let response = genericFunction(this.serviceTwo.methodTwo())
}
Modify you Generic Function by just receiving a variable.
//if it is angular 4 or less
genericFunction (method: Observable) {
return method.map(res => {
return res.json();
});
}
//if it is angular 5 or 6
genericFunction (method: Observable) {
return method.pipe(
map(res => {
return res;
}));
}
Upvotes: 0