typescript

How to get the property of an extracted union type by the value of function parameter


I am wondering if there's a way could let ts compiler determine the type of callback in useListenAPI as

const enum APIType {
    API1 = 1,
    API2 = 2
}

interface API1{
    type: APIType.API1,
    Data: number[]
}

interface API2{
    type: APIType.API2,
    Data: {
        isAdmin: boolean,
        data: number[]
    }
}

type UnionAPI = API1 | API2;

//ts error
//'eventType' refers to a value, but is being used as a type here. Did you mean 'typeof eventType'?
const useListenAPI = (eventType: APIType, callback: (data:Extract<UnionAPI,{type: eventType}>["Data"]) => void)) => {
    // do whatever
}

//'type: typeof eventType' will make callback is (data: number[]|{isAdmin: boolean, data: number[]}) => void
const useListenAPI = (eventType: APIType, callback: (data:Extract<UnionAPI,{type: typeof eventType}>["Data"]) => void)) => {
    // do whatever
}

I know it is solvable using generic like following but I am just wondering if there's a simpler version, or it is out of reach from ts compiler since it a runtime thing

const useListenAPI = <T,>(eventType: APIType, callback: (data:Extract<UnionAPI,{type: T}>["Data"]) => void)) => {
    // do whatever
}

useListenAPI<APIType.API1>(APIType.API1, (data) => {
    // data: number[]
})

Solution

  • You can use the function overloads.

    Here is the example

    
    // Overloads
    function useListenAPI(eventType: APIType.API1, callback: (data: API1['Data']) => void): void;
    function useListenAPI(eventType: APIType.API2, callback: (data: API2['Data']) => void): void;
    function useListenAPI(eventType: APIType, callback: Function) {
        // implementation details
    }
    
    // Usage
    useListenAPI(APIType.API1, (data) => {
        // data is number[]
    });
    
    useListenAPI(APIType.API2, (data) => {
        // data is { isAdmin: boolean, data: number[] }
    });
    

    Here is the doc

    https://www.typescriptlang.org/docs/handbook/2/functions.html#function-overloads