How do I export a React context component that takes a generic from an ambient module in TypeScript?

Viewed 138

I am working inside a TypeScript app that uses an NPM package that is implemented in plain JavaScript. In order to provide myself with some type safety, I am creating my own TypeScript ambient module to provide types for this library.

One of the components that is exported from this library is a React context component that should accept a generic type. This app is unfortunately not upgraded to use hooks so I'm limited to the context components. Here is an example of what I am trying to type:

import { URLStateContext } from 'some-library'

<URLStateContext.Consumer>
  {({ params }) => <div />}
</URLStateContext.Consumer>

I've declared an ambient module as such:

declare module 'some-library' {
  // ...
}

What I'd like to be able to do is type the URLStateContext component in my ambient module to accept a generic. In the above example, this generic type should apply to the params value in the consumer.

I've tried a mix of things, each with their own pros and cons, but none that I can figure out how to fully type properly.

1)

import { Context } from 'react'

declare module 'some-library' {
  export type URLStateContext<TUrlState> = Context<{ params: TUrlState }>
}

My first thought was to try the above since the return type of createContext is Context<...>. This however give me an error that states "URLStateContext only refers to a type, but is being used as a value here."

2)

import { Context } from 'react'

declare module 'some-library' {
  export const URLStateContext: Context<any>
}

This actually compiles, but here I lose the generic, which means that I either need to live with the any, or I need to try and type the child function on the consumer:

<URLStateContext.Consumer>
  {({ params }: { params: MyParams }) => <div />}
</URLStateContext.Consumer>

I'd like to be able to pass a generic to the component itself so that its callback function is typed for me correctly. Here is my ideal code:

interface Params {
  // ...
}

// `params` would automatically be typed correctly
<URLStateContext.Consumer<Params>>
  {({ params }) => <div />}
</URLStateContext.Consumer>

Any help would be appreciated!

0 Answers
Related