title: Conditional types and string literal + class union interaction
body: <p>I've came across weird interaction within the types system while trying to properly type <a href="https://emotion.sh/docs/styled#as-prop" rel="nofollow noreferrer"><code>as</code> prop from emotion</a>.</p>
  
  <pre><code>import React, { Component, FC, PropsWithChildren } from "react";
  
  // Possible types for `as` prop are tag name or component
  type AsType =
    | keyof JSX.IntrinsicElements
    | unknown
  
  
  // Infered props based on `as`
  type AsProps&lt;T extends AsType&gt; = {
    as?: T;
  } &amp; (T extends FC&lt;infer FProps&gt;
    ? FProps &amp; { fc?: "fc" }
    : T extends new (...args: any) =&gt; Component&lt;infer CProps&gt;
    ? CProps &amp; { cc?: "cc" }
    : T extends keyof JSX.IntrinsicElements
    ? JSX.IntrinsicElements[T] &amp; { tag?: "tag" }
    : { non?: "non" });
  
  const Box = &lt;T extends AsType&gt;(props: PropsWithChildren&lt;AsProps&lt;T&gt;&gt;) =&gt; null;
  
  // Test components
  class ClassComponent extends Component&lt;{ CCProp: "foo" }&gt; {}
  const FunctionComponent: FC&lt;{ FCProp: "foo" }&gt; = () =&gt; null;
  
  const Foo: FC = () =&gt; (
    &lt;&gt;
      &lt;Box as={FunctionComponent} fc="fc" FCProp="foo" /&gt;
      &lt;Box as={ClassComponent} cc="cc" CCProp="foo" /&gt;
      {/* Here types are inferred incorrectly.*/}
      {/* `tag` should be expected */}
      {/* `href` should show error that `true` is not assignable to `string` */}
      &lt;Box as="a" tag="tag" href /&gt; 
      &lt;Box non="non" /&gt;
    &lt;/&gt;
  );
  </code></pre>
  
  <p>I'm linking a <a href="https://codesandbox.io/s/react-typescript-7b4g0" rel="nofollow noreferrer">codesandbox</a> since the problem is hard to see without linting and autocomplete.</p>
  
  <p>I'm using conditional types to expect correct props based on what is in the <code>as</code> prop and for string tags the type system straight up gives up.
  If you specify the tag <code>as const</code> it works. If you don't, it's inferred as <code>string</code> and the conditional part of <code>AsProps</code> infers it as <code>non</code> type.
  While messing around I found out that for some reason f.e. if you leave out <code>unknown</code> from <code>AsType</code> the tags work even without <code>as const</code>.</p>
  
  <p>I can't figure out the correct solution.</p>