Jawensi
Jawensi

Reputation: 25

Infer callback parameter type based on function parameter

    type SeveralTypes = type0 | type1 | type2;
    function funTypes<T extends SeveralTypes>(info: T, callback: (obj: T extends type2 ? number : string) => void) {
        if (isType0(info)) {
            return callback("passAstring");  // TS Warn: Argument of type '"passAstring"' is not assignable to parameter of type 'T extends boolean ? number : string'

        } else if (isType1(info)) {
            return callback("passAstring"); // TS Warn: Argument of type '"passAstring"' is not assignable to parameter of type 'T extends boolean ? number : string'

        } else {
            return callback(1001); // TS Warn: Argument of type '1001' is not assignable to parameter of type 'T extends boolean ? number : string'
        }
    }

    funTypes(1, (d) => { });       // Typeof d --> string
    funTypes("str", (d) => { });   // Typeof d --> string
    funTypes(false, (d) => { });   // Typeof d --> number

When I use this function the infer type of the parameter callback are correct. However TS indicates problems when assigning parameters. Is there another way to type the callback parameters?

Upvotes: 1

Views: 140

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250036

There are several issues here. The major one being that narrowing one variable (info in this case) never has any impact on the type of another variable (callback) in this case. There is also the issue that generally conditional types that still contain unresolved type parameters are generally hard for the compiler to reason about, so since T is not known, and your function takes an argument dependent on T, typescript will do the safe thing and not let you use as a parameter either number or string

The usual way to get around this is to use type assertions, or my preferred approach, to use a generic public signature and a more permissive implementation signature (just be aware it is up to you to ensure the logic in your conditional type is replicated in the implementation, there is no help from the compiler here)

type SeveralTypes = type0 | type1 | type2;
function funTypes<T extends SeveralTypes>(info: T, callback: (obj: T extends type2 ? number : string) => void): void
function funTypes(info: SeveralTypes, callback: (obj: number | string) => void) {
  if (isType0(info)) {
    return callback("passAstring");

  } else if (isType1(info)) {
    return callback("passAstring");

  } else {
    return callback(1001); 
  }
}

funTypes(1, (d) => { });       // Typeof d --> string
funTypes("str", (d) => { });   // Typeof d --> string
funTypes(false, (d) => { });   // Typeof d --> number

Playground Link

Upvotes: 1

Related Questions