parcel-bundler / parcel

The zero configuration build tool for the web. πŸ“¦πŸš€
https://parceljs.org
MIT License
43.35k stars 2.26k forks source link

TypeScript library includes import("...") in type definition #7951

Open scottwillmoore opened 2 years ago

scottwillmoore commented 2 years ago

πŸ› bug report

I am building a TypeScript library for my UI components, which should include a TypeScript definition file in the output. The TypeScript definition file is generated, but for some reason some types are generated with import("...") in the type. You can see this in the example below.

πŸŽ› Configuration (.babelrc, package.json, cli command)

See the linked repository.

πŸ€” Expected Behavior

The TypeScript types do not include import("...").

😯 Current Behavior

import { ComponentPropsWithRef, ElementType, ExoticComponent, ForwardRefExoticComponent, ForwardRefRenderFunction, ForwardedRef, PropsWithChildren, ReactElement } from "react";
type As<T = {}> = ElementType<T>;
type AsElement<T extends As> = T extends keyof HTMLElementTagNameMap ? HTMLElementTagNameMap[T] : T extends keyof SVGElementTagNameMap ? SVGElementTagNameMap[T] : any;
type AsProp<T extends As> = {
    as?: T;
};
type Merge<T, U> = T & Omit<U, keyof T>;
type PropsWithAs<T extends As, P> = Merge<AsProp<T> & P, ComponentPropsWithRef<T>>;
export type ExoticComponentWithAs<T extends As, P> = ExoticComponent<PropsWithAs<T, P>> & (<U extends As>(props: PropsWithAs<U, P> & Required<AsProp<U>>) => ReactElement | null);
export type ForwardRefExoticComponentWithAs<T extends As, P> = ForwardRefExoticComponent<PropsWithAs<T, P>> & ExoticComponentWithAs<T, P>;
export type ForwardRefWithAsRenderFunction<T extends As, P> = ForwardRefRenderFunction<AsElement<T>, PropsWithAs<T, P>> & ((props: PropsWithChildren<PropsWithAs<T, P>>, ref: ForwardedRef<AsElement<T>>) => ReactElement | null);
export const forwardRefWithAs: <T extends As<{}>, P = {}>(render: ForwardRefWithAsRenderFunction<T, P>) => ForwardRefExoticComponentWithAs<T, P>;
export type ClassName = {
    [key: string]: boolean;
} | string[] | string | null | undefined;
export type BoxProps = {
    className?: ClassName;
};
export const Box: import("utilities/forwardRefWithAs").ForwardRefExoticComponentWithAs<"div", BoxProps>;
export type ButtonColor = "blue" | "gray" | "green" | "red";
export type ButtonSize = "small" | "medium" | "large";
export type ButtonProps = {
    color: ButtonColor;
    size: ButtonSize;
};
export const Button: import("utilities/forwardRefWithAs").ForwardRefExoticComponentWithAs<"button", ButtonProps>;

//# sourceMappingURL=index.d.ts.map

For example, you can see export const Button: import("utilities/forwardRefWithAs").ForwardRefExoticComponentWithAs<"button", ButtonProps>;, however it should be export const Button: ForwardRefExoticComponentWithAs<"button", ButtonProps>; (without the import statement).

πŸ’ Possible Solution

Edit: You can directly import the type, rather than it being implicitly imported alongside the function.

export const forwardRefWithAs = <T extends As, P = {}>(
    render: ForwardRefWithAsRenderFunction<T, P>
) => forwardRef(render) as ForwardRefExoticComponentWithAs<T, P>;

When the type is implicitly imported as below, it does not work.

import { forwardRefWithAs } from "../../utilities/forwardRefWithAs";
const Button = forwardRefWithAs<typeof Button, ButtonProps>(...);

When the type is directly imported (shouldn't be needed) as below, it does work.

// TypeScript: 'ForwardRefExoticComponentWithAs' is declared but its value is never read.
import { ForwardRefExoticComponentWithAs, forwardRefWithAs } from "../../utilities/forwardRefWithAs";
const Button = forwardRefWithAs<typeof Button, ButtonProps>(...);

πŸ”¦ Context

I am building a TypeScript library for my UI components, but the generated type definition file is incorrect. Therefore, when I reference these types from other packages in my repository, they appear as any.

πŸ’» Code Sample

I don't have time at the moment to create a small reproducible example, but you should be able to reproduce the issue with my repository pinned at this commit: https://github.com/scottwillmoore/ece4094/tree/627089096657f91b768f7d65f024c84a4fee102e. At the moment, the repository is almost minimal anyway. You should be able to see the issue by by running npm install at the project root, and then running npm run build at the project root, or in the packages/ui folder.

🌍 Your Environment

Software Version(s)
Node 16.14.2
NPM 8.6.0
Parcel 2.4.1
Operating System Ubuntu 20.04 on WSL

Please let me know if there is any addition information required.

scottwillmoore commented 2 years ago

I also noticed that my forwardRefWithAs function defines the return type with as (which is required in this case).

export const forwardRefWithAs = <T extends As, P = {}>(
    render: ForwardRefWithAsRenderFunction<T, P>
) => forwardRef(render) as ForwardRefExoticComponentWithAs<T, P>;

However, even with an explicit return type, it still does not work.

export const forwardRefWithAs = <T extends As, P = {}>(
    render: ForwardRefWithAsRenderFunction<T, P>
): ForwardRefExoticComponentWithAs<T, P> =>
    forwardRef(render) as ForwardRefExoticComponentWithAs<T, P>;
export function forwardRefWithAs<T extends As, P = {}>(
    render: ForwardRefWithAsRenderFunction<T, P>
): ForwardRefExoticComponentWithAs<T, P> {
    return forwardRef(render) as ForwardRefExoticComponentWithAs<T, P>;
}
martijnversluis commented 2 years ago

I'm also experiencing this issue. Any updates on this?

levino commented 1 year ago

I also noticed that my forwardRefWithAs function defines the return type with as (which is required in this case).

export const forwardRefWithAs = <T extends As, P = {}>(
  render: ForwardRefWithAsRenderFunction<T, P>
) => forwardRef(render) as ForwardRefExoticComponentWithAs<T, P>;

However, even with an explicit return type, it still does not work.

export const forwardRefWithAs = <T extends As, P = {}>(
  render: ForwardRefWithAsRenderFunction<T, P>
): ForwardRefExoticComponentWithAs<T, P> =>
  forwardRef(render) as ForwardRefExoticComponentWithAs<T, P>;
export function forwardRefWithAs<T extends As, P = {}>(
  render: ForwardRefWithAsRenderFunction<T, P>
): ForwardRefExoticComponentWithAs<T, P> {
  return forwardRef(render) as ForwardRefExoticComponentWithAs<T, P>;
}

Best workaround ever! Just always use

import * as Whatever from './whatever.ts'
github-actions[bot] commented 1 year ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 14 days if no further activity occurs.

mischnic commented 1 year ago

If this is still an issue, please share a minimal reproduction.

scottwillmoore commented 1 year ago

I don't use Parcel at the moment, and don't have the time to create a minimal reproduction, but I did provide a commit hash to a reproduction (non-minimal) in my original post.

danieltroger commented 1 year ago

Phew, this was quite some effort. Worth it if it gets fixed sometime though ;)

Here you go: parcel-d.ts-import-bug.zip

  1. cd go-here
  2. yarn
  3. cd packages/utilishared
  4. yarn build
  5. cat dist/index.d.ts

You will see the output file contains:

import { EUnavailabilityType } from "../../../../lib/api_schema/TrackV2Request";
export function send_product_unavailable_to_depict(unavailability_type: EUnavailabilityType): Promise<void>;

Which it shouldn't - parcel as bundler should make sure that everything that's not included from other packages (or even completely everything) should be bundled into the output .d.ts file