I'm trying to take advantage of react typings to implements (for learning purpuse) a view framework. Everything is well and dandy except when trying to mix a conditional type with the Omit type (to showcase it package @types/react is needed):

import {DetailedHTMLFactory, ReactHTML} from 'react'

type Tags = keyof ReactHTML

type ReactPropsFrom<Tag extends Tags> =
  ReactHTML[Tag] extends DetailedHTMLFactory<infer P, any> ? P : never

At this point I do have the properties for an HTMLElement according to react definitions by using (for anchors) ReactPropsFrom<'a'>. What is left to do is to remove the react specific properties. Let's define some of them:

type ReactSpecificProps =         |
  'defaultChecked'                |
  'defaultValue'                  |
  'suppressHydrationWarning'      |

And the Omit type

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

If i do Omit<ReactPropsFrom<'a'>, ReactSpecificProps> everything is fine and i do have in the resulting type all properties I want.

But if i do a more generic approach like so:

type Props<Tag extends Tags> = Omit<ReactPropsFrom<Tag>, ReactSpecificProps>

I end up with this error

// type ReactSpecificProps = "defaultChecked" |...
// Type '"defaultChecked"' is not assignable to type 'keyof ReactPropsFrom<Tag>'.
// Type '"defaultChecked"' is not assignable to type 'never'

I do understands it as ReactPropsFrom might returns never (even if it will never be the case as the generic passed always extends Tags), and in that eventuality the type checker break. But as I understands why I can't figure a way to avoid the issue.

Naively I hopped something like:

type Props<Tag extends Tags> = Omit<ReactPropsFrom<Tag>!, ReactSpecificProps>

with the bang ! operator (as I know it will never be a never) but it can't be used on type definition (?). Any clue ?

Thanks in advance Seb

1 Answers

Titian Cernicova-Dragomir On Best Solutions

You can go through a lot of trouble to convince the compiler that ReactPropsFrom<Tag> indeed contains ReactSpecificProps but I would advise against it. The Omit type that is coming in 3.5 does not constrain K to be a keyof T (PR and the declination to add the constraint). So the solution is to just remove the constraint on K and it will all work.

For completeness, this is what convincing the compiler would take (at least what I came up with, there may be shorter ways):

type Props<Tag extends Tags> = ReactPropsFrom<Tag> extends infer U ?
    [U] extends [Partial<Record<ReactSpecificProps, any>>] ? Omit<U, ReactSpecificProps> : never: never;