Home > Mobile >  Typescript can't narrow function signature on sum type of component props
Typescript can't narrow function signature on sum type of component props

Time:01-22

I have a component that takes two possible callbacks. The callback that should be passed depends on the other properties, if property type is passed with value A, then it should use callback A, if type is passed as B then it should use callback B. To express this dichotomy I am using a sum type for the entire props object, using the type as a tag.

I tried all kind of combinations for the tab, but I can not get TS to recognise that I'm passing the right callback.

Here is a simplification of the code:

import React from 'react'
type SessionDefinition = {name: string}
type SessionDefinitionFromDb = {name: string, id: string}

type CreateCb = (definition: SessionDefinition) => Promise<void>;
type UpdateCb = (definition: SessionDefinitionFromDb) => Promise<void>;

type Props =
  | {
      isLoading: boolean;
      onSubmit: CreateCb;
      definition: SessionDefinition;
      action: 'create';
    }
  | {
      isLoading: boolean;
      onSubmit: UpdateCb;
      definition: SessionDefinitionFromDb;
      action: 'update';
    }

export default function DefinitionForm(props: Props) {
  return <div>'Test'</div>
}
<DefinitionForm 
isLoading
onSubmit={(x:SessionDefinitionFromDb) => console.log(x)}
definition={{ id: 'test', name:'text'}}
action='update'
/

The error typescript complains about is:

Type '(x: SessionDefinitionFromDb) => void' is not assignable to type 'CreateCb | UpdateCb'.
  Type '(x: SessionDefinitionFromDb) => void' is not assignable to type 'CreateCb'.
    Types of parameters 'x' and 'definition' are incompatible.
      Property 'id' is missing in type 'SessionDefinition' but required in type 'SessionDefinitionFromDb'.

And here is a Playground example

CodePudding user response:

The error message is confusing, but the problem seems to come from the fact that your callback returns void, and not Promise<void> as expected. So replacing the said callback with (x: SessionDefinitionFromDb) => new Promise(() => console.log(x)) seems to solve it, as Typescript isn't complaining anymore for me.

CodePudding user response:

The problem is stemming from the fact that TypeScript can't narrow down the props provided to either object types. You're getting the

Property 'id' is missing in type 'SessionDefinition' but required in type 'SessionDefinitionFromDb'.

error because, after TS exhausts the type evaluation and finds it impossible, TS will start by identifying one property that's incompatible and making that the error message.

If you make the passed props compatible with the second object type - where onSubmit is an UpdateCb - it'll work.

<DefinitionForm
  isLoading
  onSubmit={(x: SessionDefinitionFromDb) => {
    console.log(x);
    return Promise.resolve();
  }}
  // ^^^^^^^^^^^^^^^
  definition={{ id: 'test', name: 'text' }}
  action='update'
/>
  •  Tags:  
  • Related