'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