1

I have an api with a typed map from api name to api input object type:

type ApiInputTypes = {
    doThis: { x: number }
    doThat: { y: string }
}

type ApiNames = keyof ApiInputTypes

My goal is to write a generic function that can handle any of these api requests, but I'm getting a type error where I would not expect it.

function handleApi<Name extends ApiNames>(name: Name, args: ApiInputTypes[Name]) {
    if (name === "doThis") {
        args.x // Error
    } else {
        args.y // Error
    }
 }

Ironically, this still works though...

// But this works...
handleApi("doThis", { x: 190 })

Any ideas? Here's a playground link.

2 Answers 2

5

Control flow analysis currently does not narrow generic type parameters (see microsoft/TypeScript#24085). It looks like this issue has been reported multiple times with multiple suggestions for dealing with it. Nothing's been implemented yet. For now there are just workarounds.

One possible workaround is to package name and args into a single value nameAndArgs, whose type you specify as a union of the two possible cases.

type ValueOf<T> = T[keyof T];
type NameAndArgs = ValueOf<{ [N in ApiNames]: { name: N, args: ApiInputTypes[N] } }>;

function handleApi<Name extends ApiNames>(name: Name, args: ApiInputTypes[Name]) {
  const nameAndArgs = { name, args } as NameAndArgs;
  if (nameAndArgs.name === "doThis") {
    nameAndArgs.args.x // okay
  } else {
    nameAndArgs.args.y // okay
  }
}

Note that the NameAndArgs property is equivalent to

type NameAndArgs = {
    name: "doThis";
    args: {
        x: number;
    };
} | {
    name: "doThat";
    args: {
        y: string;
    };
}

The function works because the single nameAndArgs variable can be narrowed, and the name and args properties stay narrowed in the relevant clauses.

Sign up to request clarification or add additional context in comments.

Comments

0

Try storing the types in an enum

export enum ApiInputTypes {
  doThis = 'doThis',
  doThat = 'doThat'
}

function handleApi(name: ApiInputTypes) {
  if(name === ApiInputTypes.doThis){
   //doThis
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.