'How to create a generic method where an argument should be a subset of a type
I'm building a react application that provide some services in a library project. These services may be used - or not - in consuming application.
I would like to create a method with an argument that must be a set of services and nothing else. I can define some Pick<> types, but I can't make it generic.
For example, I want my method to perform some init and clean up code, which are the same, then callback the requestor passing the exact services required.
To clarify, here's a repro code :
class Service1 {}
class Service2 {}
class Service3 {}
class Service4 {}
type AllServices = {
Service1 : Service1,
Service2 : Service2,
Service3 : Service3,
Service4 : Service4,
}
const someMethod = (
serviceLocator : SomeServicesCase1,
nextAction : (services : SomeServicesCase1)=>void
)=>{
console.log("Do something generic (initializing...)");
nextAction(serviceLocator); // Just forward the services
console.log("Do something else (cleanup ...)")
};
// First case : compiles because arg satisfy the type
type SomeServicesCase1 = Pick<AllServices, "Service1" | "Service2">; // My first consumer requires only service1 and service2
someMethod(
{
Service1 : new Service1(),
Service2 : new Service2()
},
(services)=>{
// only this two services are available.
console.log(services.Service1);
console.log(services.Service2);
}
)
// Second case : Does not compile, because service2 is missing and service 3 in't allowed
type SomeServicesCase2 = Pick<AllServices, "Service1" | "Service3">; // Second consumer requires service1 and service 3
someMethod(
{
Service1 : new Service1(),
Service3 : new Service3()
},
(services)=>{
console.log(services.Service1);
console.log(services.Service3);
}
)
How can I write my someMethod to accept any combination of services ? I guess generic may help but I didn't found exactly how to write it
Solution 1:[1]
I managed to find a way :
const someMethod = <TServices extends Partial<AllServices>>(
serviceLocator : TServices,
nextAction : (services : TServices)=>void
)=>{
console.log("Do something generic (initializing...)");
nextAction(serviceLocator);
console.log("Do something else (cleanup ...)")
};
I understand this to accept any type that is a partial of my "allServices" type.
I could also be even more simple by using <TServices extends object>. In this case, I don't have to know services in advance.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Steve B |
