1

I have a hook that returns data. Its type is depending on the props passed.

interface MyHookProps<T, TF> {
  transform?: (data: T) => TF
}

const myHook = <T extends any = any, TF = any>(props?: MyHookProps<T, TF>) => {
  const [items, setItems] = useState([])

  return { data: items }
}

I want my data to be the return type of transform when it is passed as prop and T when its not

example:

interface User {
  name: string;
  age: number;
}

const { data } = myHook<User[]>();

Its type should be User[], but when you do it like this:

const { data } = myHook<User[]>({
  transform: (data) => data.map(d => ({ ...d, another: 1 }))
})

Its type should be

{
  name: string;
  age: number;
  another: string;
}[]

1 Answer 1

1

This is currently not possible as written in the question as TypeScript lacks partial type inference. Providing an explicit type parameter will prevent inference of others.

We can use currying to work around this problem.

const myHook = <T extends any>() => <TF extends any>(props?: MyHookProps<T, TF>) => {
  const [items, setItems] = useState([])

  return { data: items } as { data: unknown extends TF ? T : TF }
}

Here we "chain" two functions together. The first one uses T as the generic type while the second one can infer TF. In the conditional type at the return statement, we check if TF could be inferred. If not, we return T.

interface User {
  name: string;
  age: number;
}

namespace N1 {
  const { data } = myHook<User[]>()();
}

namespace N2 {
  const { data } = myHook<User[]>()({
    transform: (data) => data.map(d => ({ ...d, another: 1 }))
  })
}

Playground

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

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.