skip to Main Content

I’m a typescript newbie.

Description

I have a typescript react application, and now I’ve had a problem that I need to dynamic watch the values comes out of the watch() from react-hook-from based on the different param that passed into my customized hook.

which means from cosumer side

when first param is 'k', typescript should be automatcally give suggestion and the type should be interface kq.

when first one is 'c', typescript should be automatcally give suggestion and the type should be interface cq.

I tried the below solution type pqp<T> = T extends k ? kq : T extends c ? cq : never;, but it seems union 'kq' and 'cq', no matter what is the first param, it always suggest 's' and 'ce', which exists both in 'kq' and 'cq'.

I’ve been also trying a lot of solution on the internet, but it seems not working…post here ask for help.

Hook Code Block

import { useMemo } from 'react';
import { useForm } from 'react-hook-form'

type k = 'k';
type c = 'c';

type p = k | c;

interface kq {
  s: string;
  t: string;
  ce: string;
}

interface cq {
  s: string;
  c: string;
  ce: string;
}

type pqp<T> = T extends k ? kq : T extends c ? cq : never;

const useMyHook = <T extends p>(product: T, qp?: pqp<T>) => {
  const generatedQP = useMemo(() => {
    if (product === 'c') {
      return ({
        will_return_s: qp?.s || '',
        will_return_ce: qp?.ce || 'default',
        will_return_c: qp?.c || '' // ERROR! property c does not exist on kq or cq. property c doesn not exist on kq
      }); 
    } else {
      return ({
        will_return_s: qp?.s || '',
        will_return_ce: qp?.ce || 'default',
        will_return_c: qp?.t || '' // ERROR! property t does not exist on kq or cq. property t doesn not exist on cq
      }); 
    }
  }, [product]);

  const methods = useForm({
    criterialMode: 'all',
    defaultValues: generatedQP
  })

  return methods;
}

Consumer Code Block

function component() {
  const p: p = 'c';
  const { watch } = useMyHook(p)

  const {/** these will change and report error based on what p is */} = watch();
  // I also noticed that the type is always 'kq' for watch();
}

2

Answers


  1. You can use an enum to store the values.

    enum Types {
      k = 'k',
      c = 'c',
    }
    
    interface kq {}
    
    interface cq {}
    
    type pqp<T> = T extends Types.k ? kq : T extends Types.c ? cq : never;
    
    const useMyHook = <T extends `${Types}`>(product: T, qp?: pqp<T>) => {
      return {
        watch: () => qp,
      };
    };
    
    function component() {
      const p = Types.k;
    
      const { watch } = useMyHook(p);
      console.log(watch);
    }
    

    enter image description here

    Login or Signup to reply.
  2. You can use a type guard to narrow type of qp when you use it:

    // if product is 'c' than qp is cq, otherwise it's kp
    const isCq = (product: c | k, qp?: kq | cq): qp is cq => product === "c";
    
    // define the arguments as unions
    const useMyHook = (product: c | k, qp?: kq | cq) => {
      const generatedQP = useMemo(() => {
        if (isCq(product, qp)) { // use the guard to narrow the type of `qp`
          return {
            will_return_s: qp?.s || "",
            will_return_ce: qp?.ce || "default",
            will_return_c: qp?.c || "",
          };
        } else {
          return {
            will_return_s: qp?.s || "",
            will_return_ce: qp?.ce || "default",
            will_return_c: qp?.t || "",
          };
        }
      }, [product, qp]);
    
      const methods = useForm({
        criteriaMode: "all",
        defaultValues: generatedQP,
      });
    
      return methods;
    };
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search