Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

regression in 5.4: error TS2604: JSX element type 'Cmp' does not have any construct or call signatures. #57862

Closed
wmertens opened this issue Mar 20, 2024 · 9 comments

Comments

@wmertens
Copy link

πŸ”Ž Search Terms

TS2604

πŸ•— Version & Regression Information

This is a regression, it started happening with 5.4.0

Note that that comment lists 2 issues, now there's only the one TS2604 issue.

The issue still exists in @next.

⏯ Playground Link

https://www.typescriptlang.org/play?#code/PTAEEsBcHIGdQIoHdwGtQCMCulQHtUBYAKHAFsAHPAJ1wCUBTAQwGNcAzavM0aa5ttBIkQoACoALcPGmh+ARyzh+AE1DsaoSBIYRYsLAwA0oFhKYA7AObhrEXJDxyGkLNQvjQZJqgYzcTKAq4OzsDPwWuOFc1CSQAJ4UugDyGABWDGwAClwUsAA8YgB8oAC8JKCVngwAHpAMFirwjCw0KvmW8SadRRVVAPzifZUAXKAWDABu4QDcwsQJSaAAYlgWbOB4FgDC3FQTkflZZaBrqBZ4SBYlpaAAFBS5sGNZAJRlJZ1zxCJgsJDUWxWAA+7A8sgUSlU6k02l00gMxlM5msQPsWicWwANvFkZl0BpqOoPN5fP5QIFgqFwg0otQYnFErocng8sl2PltskALJZG6gLm80C1eqNeC+eJ4digABSAGUABoAOgAkpFARZYOAWABRLEMMi02DDUCDeXKtUA2xa3X6w2RWAAbUFWQAuiaxi7hXUGk0VmsNltdpQtrT8rYwkTkoCrCy8r1iFVTaBUhlsk98tHwLGngmk2MJtNYj9iK1NbgsngcWUTZzvaK-f8NVZQMD-etIJsdntQ5ETk20W3Vh2u8H9rSincAN4mpNMZ4CygnaDBSbQCnwbZGWdVJV7x6s42JqoAXzGU43-U9oBPoAAZKA47B2fkB3YRb7N8mV+A16BPUU7ylCUM7HkmlSiJIsjBCoFjQHSMQQB4ACsSoAMzoZgODCvSNAyChSoACxKgADDulT8K47igJyS5TnuSoHnkJ5FFI+TANslBFN84FVKI4EAHr9CaJ7zEAA

πŸ’» Code

// it's Qwik but ok
import React from 'react'

// This is required for the issue, changing it to return T makes it a different error
type ObjectProps<T> =
    T extends Record<any, any>
    ? T
    : never;

type FunctionComponent<P = unknown> = (props: P) => any;

// string|fn is required for the issue, changing it to only check for fn makes it a different error
type PropsOf<COMP> = COMP extends keyof JSX.IntrinsicElements
    ? JSX.IntrinsicElements[COMP]
    : COMP extends FunctionComponent<infer OrigProps>
    ? ObjectProps<OrigProps>
    : never

const Poly =
    <C extends string | FunctionComponent = string | FunctionComponent>({
        as: Cmp = 'div' as C,
        ...props
    }: { as?: C } & PropsOf<string extends C ? 'div' : C>) => {
        // This didn't error in 5.3.3 but errors in 5.4.0
        return <Cmp {...props}>hi</Cmp>;
        //      ^?
    }

In 5.3.3, the type of Cmp is roughly String|FunctionComponent, and in 5.4.2 it's C|(C&string)

πŸ™ Actual behavior

It can't determine that Cmp is a function

πŸ™‚ Expected behavior

It should determine that Cmp is a valid JSX tag

Additional information about the issue

No response

@DanielRosenwasser DanielRosenwasser added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Working as Intended The behavior described is the intended behavior; this is not a bug labels Mar 20, 2024
@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Mar 20, 2024

Don't know how that label got added - I haven't gotten the chance to actually look at this issue. My apologies.

@RyanCavanaugh
Copy link
Member

#57117 (comment)

@wmertens
Copy link
Author

@RyanCavanaugh yes but that error doesn't appear any more, only the 2604 one

@RyanCavanaugh
Copy link
Member

This is, for all intents and purposes, a correct error that was missed in 5.3. Prior versions were incorrectly (within the bounds of correct analysis that actually exist) allowing the call, though I'm still not exactly sure why.

Further reduced (bisect on this one is still #56004):

declare const React: any;
namespace JSX {
  export interface IntrinsicElements {
    div: {
      name: string;
      as?: "div";
    };
  }
}

// This is required for the issue, changing it to return T makes it a different error
type ObjectProps<T> = T extends Record<any, any> ? T : never;
type FunctionComponent<P> = (props: P) => any;
// string|fn is required for the issue, changing it to only check for fn makes it a different error
type PropsOf<COMP> = COMP extends keyof JSX.IntrinsicElements
  ? JSX.IntrinsicElements[COMP]
  : COMP extends FunctionComponent<infer OrigProps>
  ? ObjectProps<OrigProps & Record<any, any>>
  : never

const Poly1 = <C extends string | FunctionComponent<unknown>>(thing: { as: C } & PropsOf<C>) => {
  const Cmp = thing.as;
  const p1 = <Cmp {...(null as any)} />;;
  if (typeof Cmp === "string") {
    const p2 = <Cmp {...(null as any)} />;
  } else {
    const p3 = <Cmp {...(null as any)} />;
  }
}

Poly1({ as: "div", name: "foo" });

In 5.3, p2 is an error, but p1 isn't. That doesn't make any sense -- if p2 is an error, then p1 has to be one too. In 5.4 they're consistently both errors.

Correct analysis of this function basically boils down to being able to reason about the fact that

Poly1<"baz">(...);

can't actually happen since if C = baz then props = never, but TS has never had that capability.

@wmertens
Copy link
Author

I'm a bit lost - so you're saying that this polymorphic behavior should never have worked and cannot work?

@wmertens
Copy link
Author

BTW in the original implementation if C=="Baz" then the props would be the ones from span

@wmertens
Copy link
Author

@RyanCavanaugh I'm not sure what to do here - is a polymorphic component impossible, or are we doing it wrongly?

@wmertens
Copy link
Author

Ok - with some inspiration here I managed to make it pass in newer TS versions, will merge soon. Since I understand that it was failing correctly, I'll close this issue.

@RyanCavanaugh
Copy link
Member

Sorry for the delay btw, we were double-checking a few other things to make sure this wasn't secretly a bug of some kind. Glad to hear you got an acceptable solution on your end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants