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

TypeScript Error only on big type only when assigned to a variable #58271

Open
tmax22 opened this issue Apr 21, 2024 · 3 comments
Open

TypeScript Error only on big type only when assigned to a variable #58271

tmax22 opened this issue Apr 21, 2024 · 3 comments
Labels
Needs More Info The issue still hasn't been fully clarified

Comments

@tmax22
Copy link

tmax22 commented Apr 21, 2024

πŸ”Ž Search Terms

TypeScript Error only on big type only when assigned to a variable

πŸ•— Version & Regression Information

typescript v5.4.5

⏯ Playground Link

https://stackblitz.com/edit/vitejs-vite-mcran5?file=src%2FLink.tsx

πŸ’» Code

copy paste from https://stackoverflow.com/questions/78252365/typescript-error-only-on-big-type-only-when-assigned-to-a-variable

πŸ™ Actual behavior

even after many years of writing typescript you sometimes get unexpected results:

assume generic type function WithComponentOverride whose purpose is accepting component props and another generic parameter which tells which ComponentType the component is:

export type WithComponentOverride<P, T extends React.ElementType> = P & {
  component: T;
} & InferProps<T>;

export type InferProps<T extends React.ElementType> =
  T extends React.ComponentType<infer P> ? P : {};

and usage:

interface MyComponentProps {
  someProp: number;
  // ... other props
}

const MyComponent = <T extends React.ElementType>(
  props: WithComponentOverride<MyComponentProps, T>,
) => {
  const Component = props.component;
  return <Component {...props} />;
};

now other components can use MyComponent and decide which Host element the root of the component would be in typesafe way:

import { Box } from "@mui/material";

const UsingMyComponent = () => {
  return (
    <MyComponent
      component={Box}
      someProp={5}
      sx={{ height: 10 }} // <-- perfectly typed! 
    />
  );
};

works perfectly.

now let's try to apply this generic type to a much more complicated type, taken from the component from @tanstack/react-router

import { Link as TanstackLink } from "@tanstack/react-router";

type TanstackLinkProps = Parameters<typeof TanstackLink>[0];

const Link = <T extends React.ElementType>(
  props: WithComponentOverride<TanstackLinkProps, T>,
) => {
  const component = props.component; // TS2339: Property component does not exist on type <...blabla...>
  // ...
};

we get TS2339 error even though we shouldn't get one.

we can even see { component: T; } right there in the type:

Property 'component' does not exist on type '{ ...HUGE_OBJECT_TRIMMED... } & { component: T; } & InferProps<T>'.

what is even stranger is that when accessing the type directly(and not via assigned variable) works as expected:

const Link = <T extends React.ElementType>(
  props: WithComponentOverride<TanstackLinkProps, T>,
) => {
  type Props = typeof props;
  type Component = Props["component"];  // <-- works!
  const component = props.component; // <-- Fail! TS2339: Property component does not exist on type <...blabla...>
  ...
}

WHAT'S GOING ON? I genuinely think that is a typescript bug here, but I can't tell for sure.


for the curious, here is the full typescript error:

Property 'component' does not exist on type '{ cite?: string | undefined; data?: string | undefined; form?: string | undefined; label?: string | undefined; search?: any; slot?: string | undefined; span?: number | undefined; style?: CSSProperties | undefined; summary?: string | undefined; title?: string | undefined; mask?: ToMaskOptions<AnyRoute, any, string> | undefined; pattern?: string | undefined; to?: any; params?: true | ParamsReducer<any, MakeDifferenceOptional<any, any>> | undefined; hash?: true | Updater<string> | undefined; state?: true | NonNullableUpdater<HistoryState> | undefined; from?: any; replace?: boolean | undefined; resetScroll?: boolean | undefined; startTransition?: boolean | undefined; target?: string | undefined; activeOptions?: ActiveOptions | undefined; preload?: false | "intent" | undefined; preloadDelay?: number | undefined; disabled?: boolean | undefined; activeProps?: AnchorHTMLAttributes<HTMLAnchorElement> | (() => AnchorHTMLAttributes<HTMLAnchorElement>) | undefined; inactiveProps?: AnchorHTMLAttributes<HTMLAnchorElement> | (() => AnchorHTMLAttributes<HTMLAnchorElement>) | undefined; children?: ReactNode | ((state: { isActive: boolean; }) => ReactNode); accept?: string | undefined; acceptCharset?: string | undefined; action?: string | undefined; allowFullScreen?: boolean | undefined; allowTransparency?: boolean | undefined; alt?: string | undefined; as?: string | undefined; async?: boolean | undefined; autoComplete?: string | undefined; autoPlay?: boolean | undefined; capture?: boolean | "user" | "environment" | undefined; cellPadding?: string | number | undefined; cellSpacing?: string | number | undefined; charSet?: string | undefined; challenge?: string | undefined; checked?: boolean | undefined; classID?: string | undefined; cols?: number | undefined; colSpan?: number | undefined; controls?: boolean | undefined; coords?: string | undefined; crossOrigin?: CrossOrigin; dateTime?: string | undefined; default?: boolean | undefined; defer?: boolean | undefined; download?: any; encType?: string | undefined; formAction?: string | undefined; formEncType?: string | undefined; formMethod?: string | undefined; formNoValidate?: boolean | undefined; formTarget?: string | undefined; frameBorder?: string | number | undefined; headers?: string | undefined; height?: string | number | undefined; high?: number | undefined; href?: string | undefined; hrefLang?: string | undefined; htmlFor?: string | undefined; httpEquiv?: string | undefined; integrity?: string | undefined; keyParams?: string | undefined; keyType?: string | undefined; kind?: string | undefined; list?: string | undefined; loop?: boolean | undefined; low?: number | undefined; manifest?: string | undefined; marginHeight?: number | undefined; marginWidth?: number | undefined; max?: string | number | undefined; maxLength?: number | undefined; media?: string | undefined; mediaGroup?: string | undefined; method?: string | undefined; min?: string | number | undefined; minLength?: number | undefined; multiple?: boolean | undefined; muted?: boolean | undefined; name?: string | undefined; noValidate?: boolean | undefined; open?: boolean | undefined; optimum?: number | undefined; placeholder?: string | undefined; playsInline?: boolean | undefined; poster?: string | undefined; readOnly?: boolean | undefined; required?: boolean | undefined; reversed?: boolean | undefined; rows?: number | undefined; rowSpan?: number | undefined; sandbox?: string | undefined; scope?: string | undefined; scoped?: boolean | undefined; scrolling?: string | undefined; seamless?: boolean | undefined; selected?: boolean | undefined; shape?: string | undefined; size?: number | undefined; sizes?: string | undefined; src?: string | undefined; srcDoc?: string | undefined; srcLang?: string | undefined; srcSet?: string | undefined; start?: number | undefined; step?: string | number | undefined; type?: string | undefined; useMap?: string | undefined; value?: string | number | readonly string[] | undefined; width?: string | number | undefined; wmode?: string | undefined; wrap?: string | undefined; defaultChecked?: boolean | undefined; defaultValue?: string | number | readonly string[] | undefined; suppressContentEditableWarning?: boolean | undefined; suppressHydrationWarning?: boolean | undefined; accessKey?: string | undefined; autoFocus?: boolean | undefined; className?: string | undefined; contentEditable?: Booleanish | "inherit" | "plaintext-only" | undefined; contextMenu?: string | undefined; dir?: string | undefined; draggable?: Booleanish | undefined; hidden?: boolean | undefined; id?: string | undefined; lang?: string | undefined; nonce?: string | undefined; spellCheck?: Booleanish | undefined; tabIndex?: number | undefined; translate?: "yes" | "no" | undefined; radioGroup?: string | undefined; role?: AriaRole | undefined; about?: string | undefined; content?: string | undefined; datatype?: string | undefined; inlist?: any; prefix?: string | undefined; property?: string | undefined; rel?: string | undefined; resource?: string | undefined; rev?: string | undefined; typeof?: string | undefined; vocab?: string | undefined; autoCapitalize?: string | undefined; autoCorrect?: string | undefined; autoSave?: string | undefined; color?: string | undefined; itemProp?: string | undefined; itemScope?: boolean | undefined; itemType?: string | undefined; itemID?: string | undefined; itemRef?: string | undefined; results?: number | undefined; security?: string | undefined; unselectable?: "on" | "off" | undefined; inputMode?: "search" | "text" | "none" | "tel" | "url" | "email" | "numeric" | "decimal" | undefined; is?: string | undefined; "aria-activedescendant"?: string | undefined; "aria-atomic"?: Booleanish | undefined; "aria-autocomplete"?: "list" | "none" | "inline" | "both" | undefined; "aria-braillelabel"?: string | undefined; "aria-brailleroledescription"?: string | undefined; "aria-busy"?: Booleanish | undefined; "aria-checked"?: boolean | "true" | "false" | "mixed" | undefined; "aria-colcount"?: number | undefined; "aria-colindex"?: number | undefined; "aria-colindextext"?: string | undefined; "aria-colspan"?: number | undefined; "aria-controls"?: string | undefined; "aria-current"?: boolean | "time" | "step" | "true" | "false" | "page" | "location" | "date" | undefined; "aria-describedby"?: string | undefined; "aria-description"?: string | undefined; "aria-details"?: string | undefined; "aria-disabled"?: Booleanish | undefined; "aria-dropeffect"?: "link" | "none" | "copy" | "execute" | "move" | "popup" | undefined; "aria-errormessage"?: string | undefined; "aria-expanded"?: Booleanish | undefined; "aria-flowto"?: string | undefined; "aria-grabbed"?: Booleanish | undefined; "aria-haspopup"?: boolean | "dialog" | "menu" | "true" | "false" | "grid" | "listbox" | "tree" | undefined; "aria-hidden"?: Booleanish | undefined; "aria-invalid"?: boolean | "true" | "false" | "grammar" | "spelling" | undefined; "aria-keyshortcuts"?: string | undefined; "aria-label"?: string | undefined; "aria-labelledby"?: string | undefined; "aria-level"?: number | undefined; "aria-live"?: "off" | "assertive" | "polite" | undefined; "aria-modal"?: Booleanish | undefined; "aria-multiline"?: Booleanish | undefined; "aria-multiselectable"?: Booleanish | undefined; "aria-orientation"?: "horizontal" | "vertical" | undefined; "aria-owns"?: string | undefined; "aria-placeholder"?: string | undefined; "aria-posinset"?: number | undefined; "aria-pressed"?: boolean | "true" | "false" | "mixed" | undefined; "aria-readonly"?: Booleanish | undefined; "aria-relevant"?: "text" | "additions" | "additions removals" | "additions text" | "all" | "removals" | "removals additions" | "removals text" | "text additions" | "text removals" | undefined; "aria-required"?: Booleanish | undefined; "aria-roledescription"?: string | undefined; "aria-rowcount"?: number | undefined; "aria-rowindex"?: number | undefined; "aria-rowindextext"?: string | undefined; "aria-rowspan"?: number | undefined; "aria-selected"?: Booleanish | undefined; "aria-setsize"?: number | undefined; "aria-sort"?: "none" | "ascending" | "descending" | "other" | undefined; "aria-valuemax"?: number | undefined; "aria-valuemin"?: number | undefined; "aria-valuenow"?: number | undefined; "aria-valuetext"?: string | undefined; dangerouslySetInnerHTML?: { __html: string | TrustedHTML; } | undefined; onCopy?: ClipboardEventHandler<"a"> | undefined; onCopyCapture?: ClipboardEventHandler<"a"> | undefined; onCut?: ClipboardEventHandler<"a"> | undefined; onCutCapture?: ClipboardEventHandler<"a"> | undefined; onPaste?: ClipboardEventHandler<"a"> | undefined; onPasteCapture?: ClipboardEventHandler<"a"> | undefined; onCompositionEnd?: CompositionEventHandler<"a"> | undefined; onCompositionEndCapture?: CompositionEventHandler<"a"> | undefined; onCompositionStart?: CompositionEventHandler<"a"> | undefined; onCompositionStartCapture?: CompositionEventHandler<"a"> | undefined; onCompositionUpdate?: CompositionEventHandler<"a"> | undefined; onCompositionUpdateCapture?: CompositionEventHandler<"a"> | undefined; onFocus?: FocusEventHandler<"a"> | undefined; onFocusCapture?: FocusEventHandler<"a"> | undefined; onBlur?: FocusEventHandler<"a"> | undefined; onBlurCapture?: FocusEventHandler<"a"> | undefined; onChange?: FormEventHandler<"a"> | undefined; onChangeCapture?: FormEventHandler<"a"> | undefined; onBeforeInput?: FormEventHandler<"a"> | undefined; onBeforeInputCapture?: FormEventHandler<"a"> | undefined; onInput?: FormEventHandler<"a"> | undefined; onInputCapture?: FormEventHandler<"a"> | undefined; onReset?: FormEventHandler<"a"> | undefined; onResetCapture?: FormEventHandler<"a"> | undefined; onSubmit?: FormEventHandler<"a"> | undefined; onSubmitCapture?: FormEventHandler<"a"> | undefined; onInvalid?: FormEventHandler<"a"> | undefined; onInvalidCapture?: FormEventHandler<"a"> | undefined; onLoad?: ReactEventHandler<"a"> | undefined; onLoadCapture?: ReactEventHandler<"a"> | undefined; onError?: ReactEventHandler<"a"> | undefined; onErrorCapture?: ReactEventHandler<"a"> | undefined; onKeyDown?: KeyboardEventHandler<"a"> | undefined; onKeyDownCapture?: KeyboardEventHandler<"a"> | undefined; onKeyPress?: KeyboardEventHandler<"a"> | undefined; onKeyPressCapture?: KeyboardEventHandler<"a"> | undefined; onKeyUp?: KeyboardEventHandler<"a"> | undefined; onKeyUpCapture?: KeyboardEventHandler<"a"> | undefined; onAbort?: ReactEventHandler<"a"> | undefined; onAbortCapture?: ReactEventHandler<"a"> | undefined; onCanPlay?: ReactEventHandler<"a"> | undefined; onCanPlayCapture?: ReactEventHandler<"a"> | undefined; onCanPlayThrough?: ReactEventHandler<"a"> | undefined; onCanPlayThroughCapture?: ReactEventHandler<"a"> | undefined; onDurationChange?: ReactEventHandler<"a"> | undefined; onDurationChangeCapture?: ReactEventHandler<"a"> | undefined; onEmptied?: ReactEventHandler<"a"> | undefined; onEmptiedCapture?: ReactEventHandler<"a"> | undefined; onEncrypted?: ReactEventHandler<"a"> | undefined; onEncryptedCapture?: ReactEventHandler<"a"> | undefined; onEnded?: ReactEventHandler<"a"> | undefined; onEndedCapture?: ReactEventHandler<"a"> | undefined; onLoadedData?: ReactEventHandler<"a"> | undefined; onLoadedDataCapture?: ReactEventHandler<"a"> | undefined; onLoadedMetadata?: ReactEventHandler<"a"> | undefined; onLoadedMetadataCapture?: ReactEventHandler<"a"> | undefined; onLoadStart?: ReactEventHandler<"a"> | undefined; onLoadStartCapture?: ReactEventHandler<"a"> | undefined; onPause?: ReactEventHandler<"a"> | undefined; onPauseCapture?: ReactEventHandler<"a"> | undefined; onPlay?: ReactEventHandler<"a"> | undefined; onPlayCapture?: ReactEventHandler<"a"> | undefined; onPlaying?: ReactEventHandler<"a"> | undefined; onPlayingCapture?: ReactEventHandler<"a"> | undefined; onProgress?: ReactEventHandler<"a"> | undefined; onProgressCapture?: ReactEventHandler<"a"> | undefined; onRateChange?: ReactEventHandler<"a"> | undefined; onRateChangeCapture?: ReactEventHandler<"a"> | undefined; onResize?: ReactEventHandler<"a"> | undefined; onResizeCapture?: ReactEventHandler<"a"> | undefined; onSeeked?: ReactEventHandler<"a"> | undefined; onSeekedCapture?: ReactEventHandler<"a"> | undefined; onSeeking?: ReactEventHandler<"a"> | undefined; onSeekingCapture?: ReactEventHandler<"a"> | undefined; onStalled?: ReactEventHandler<"a"> | undefined; onStalledCapture?: ReactEventHandler<"a"> | undefined; onSuspend?: ReactEventHandler<"a"> | undefined; onSuspendCapture?: ReactEventHandler<"a"> | undefined; onTimeUpdate?: ReactEventHandler<"a"> | undefined; onTimeUpdateCapture?: ReactEventHandler<"a"> | undefined; onVolumeChange?: ReactEventHandler<"a"> | undefined; onVolumeChangeCapture?: ReactEventHandler<"a"> | undefined; onWaiting?: ReactEventHandler<"a"> | undefined; onWaitingCapture?: ReactEventHandler<"a"> | undefined; onAuxClick?: MouseEventHandler<"a"> | undefined; onAuxClickCapture?: MouseEventHandler<"a"> | undefined; onClick?: MouseEventHandler<"a"> | undefined; onClickCapture?: MouseEventHandler<"a"> | undefined; onContextMenu?: MouseEventHandler<"a"> | undefined; onContextMenuCapture?: MouseEventHandler<"a"> | undefined; onDoubleClick?: MouseEventHandler<"a"> | undefined; onDoubleClickCapture?: MouseEventHandler<"a"> | undefined; onDrag?: DragEventHandler<"a"> | undefined; onDragCapture?: DragEventHandler<"a"> | undefined; onDragEnd?: DragEventHandler<"a"> | undefined; onDragEndCapture?: DragEventHandler<"a"> | undefined; onDragEnter?: DragEventHandler<"a"> | undefined; onDragEnterCapture?: DragEventHandler<"a"> | undefined; onDragExit?: DragEventHandler<"a"> | undefined; onDragExitCapture?: DragEventHandler<"a"> | undefined; onDragLeave?: DragEventHandler<"a"> | undefined; onDragLeaveCapture?: DragEventHandler<"a"> | undefined; onDragOver?: DragEventHandler<"a"> | undefined; onDragOverCapture?: DragEventHandler<"a"> | undefined; onDragStart?: DragEventHandler<"a"> | undefined; onDragStartCapture?: DragEventHandler<"a"> | undefined; onDrop?: DragEventHandler<"a"> | undefined; onDropCapture?: DragEventHandler<"a"> | undefined; onMouseDown?: MouseEventHandler<"a"> | undefined; onMouseDownCapture?: MouseEventHandler<"a"> | undefined; onMouseEnter?: MouseEventHandler<"a"> | undefined; onMouseLeave?: MouseEventHandler<"a"> | undefined; onMouseMove?: MouseEventHandler<"a"> | undefined; onMouseMoveCapture?: MouseEventHandler<"a"> | undefined; onMouseOut?: MouseEventHandler<"a"> | undefined; onMouseOutCapture?: MouseEventHandler<"a"> | undefined; onMouseOver?: MouseEventHandler<"a"> | undefined; onMouseOverCapture?: MouseEventHandler<"a"> | undefined; onMouseUp?: MouseEventHandler<"a"> | undefined; onMouseUpCapture?: MouseEventHandler<"a"> | undefined; onSelect?: ReactEventHandler<"a"> | undefined; onSelectCapture?: ReactEventHandler<"a"> | undefined; onTouchCancel?: TouchEventHandler<"a"> | undefined; onTouchCancelCapture?: TouchEventHandler<"a"> | undefined; onTouchEnd?: TouchEventHandler<"a"> | undefined; onTouchEndCapture?: TouchEventHandler<"a"> | undefined; onTouchMove?: TouchEventHandler<"a"> | undefined; onTouchMoveCapture?: TouchEventHandler<"a"> | undefined; onTouchStart?: TouchEventHandler<"a"> | undefined; onTouchStartCapture?: TouchEventHandler<"a"> | undefined; onPointerDown?: PointerEventHandler<"a"> | undefined; onPointerDownCapture?: PointerEventHandler<"a"> | undefined; onPointerMove?: PointerEventHandler<"a"> | undefined; onPointerMoveCapture?: PointerEventHandler<"a"> | undefined; onPointerUp?: PointerEventHandler<"a"> | undefined; onPointerUpCapture?: PointerEventHandler<"a"> | undefined; onPointerCancel?: PointerEventHandler<"a"> | undefined; onPointerCancelCapture?: PointerEventHandler<"a"> | undefined; onPointerEnter?: PointerEventHandler<"a"> | undefined; onPointerLeave?: PointerEventHandler<"a"> | undefined; onPointerOver?: PointerEventHandler<"a"> | undefined; onPointerOverCapture?: PointerEventHandler<"a"> | undefined; onPointerOut?: PointerEventHandler<"a"> | undefined; onPointerOutCapture?: PointerEventHandler<"a"> | undefined; onGotPointerCapture?: PointerEventHandler<"a"> | undefined; onGotPointerCaptureCapture?: PointerEventHandler<"a"> | undefined; onLostPointerCapture?: PointerEventHandler<"a"> | undefined; onLostPointerCaptureCapture?: PointerEventHandler<"a"> | undefined; onScroll?: UIEventHandler<"a"> | undefined; onScrollCapture?: UIEventHandler<"a"> | undefined; onWheel?: WheelEventHandler<"a"> | undefined; onWheelCapture?: WheelEventHandler<"a"> | undefined; onAnimationStart?: AnimationEventHandler<"a"> | undefined; onAnimationStartCapture?: AnimationEventHandler<"a"> | undefined; onAnimationEnd?: AnimationEventHandler<"a"> | undefined; onAnimationEndCapture?: AnimationEventHandler<"a"> | undefined; onAnimationIteration?: AnimationEventHandler<"a"> | undefined; onAnimationIterationCapture?: AnimationEventHandler<"a"> | undefined; onTransitionEnd?: TransitionEventHandler<"a"> | undefined; onTransitionEndCapture?: TransitionEventHandler<"a"> | undefined; key?: Key | null | undefined; ref?: LegacyRef<HTMLAnchorElement> | undefined; } & { component: T; } & InferProps<T>'.

the only difference between TanstackLinkProps and MyComponentProps is the size of the type. WithComponentOverride should act the same on both of them.

Dont belive me? heres a demo

πŸ™‚ Expected behavior

.

Additional information about the issue

No response

@RyanCavanaugh
Copy link
Member

It's not the size; using a very very large props type as the base doesn't reproduce the problem

type BaseProps = Record<`${'a' | 'b' | 'c' | 'd'}${'a' | 'b' | 'c' | 'd'}${'a' | 'b' | 'c' | 'd'}${'a' | 'b' | 'c' | 'd'}${'a' | 'b' | 'c' | 'd'}`, string>;
const Link = <T extends React.ElementType>(
  props: WithComponentOverride<BaseProps, T>
) => {
  const Component = props.component; // ok
  return <Component {...props} />;
};

Need to figure out what's weird about TanstackLinkProps but it's a tangled web

@RyanCavanaugh
Copy link
Member

Marking off some progress here...

import React from 'react';
import { RouteByPath, } from "@tanstack/react-router";
import { AnyRoute } from "@tanstack/react-router";

export type CheckPath<TRouteTree extends AnyRoute> =
  RouteByPath<TRouteTree, `${string}/`> extends never ? { to: string } : {};

export type ActiveLinkOptions<TRouteTree extends AnyRoute> = {
  to: string;
} & CheckPath<TRouteTree>;

export type WithComponentOverride<P, T extends React.ElementType> = P & InferProps<T> & {
  component: T;
};

export type InferProps<T extends React.ElementType> =
  T extends React.ComponentType<infer P> ? P : {};

declare const Link2: LinkComponent;
type Orig_TanstackLinkProps = Parameters<typeof Link2>[0];

const Link = <T extends React.ElementType>(
  props: WithComponentOverride<Orig_TanstackLinkProps, T>
) => {
  const p: keyof typeof props = 'component';
  const Component = props.component; // TS2339: Property component does not exist on type <...blabla...>
  // ...
  return <Component {...props} />;
};

export type LinkComponent = <TRouteTree extends AnyRoute>(props: ActiveLinkOptions<TRouteTree>) => React.ReactElement;

@RyanCavanaugh
Copy link
Member

I've gotten this far but we need to cut our losses trying to reduce Route and its twenty-one type parameters down to something that can be investigated.

import { Route } from '@tanstack/react-router';
import React from 'react';

export interface AnyRoute extends Route<any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any, any> {
}

export type ParseRoute<TRouteTree, TAcc = TRouteTree> = TRouteTree extends {
  types: {
    children: infer TChildren;
  };
} ? TChildren extends Array<unknown> ? ParseRoute<TChildren[number], TAcc | TChildren[number]> : TAcc : TAcc;

export type RoutesByPath<TRouteTree extends AnyRoute> = {
  [K in ParseRoute<TRouteTree> as K['fullPath']]: K;
} & Record<'.' | '..', ParseRoute<TRouteTree>>;

export type RouteByPath<TRouteTree extends AnyRoute, TPath> = (string extends TPath ? ParseRoute<TRouteTree> : RoutesByPath<TRouteTree>[TPath]);
export type CheckPath<TRouteTree extends AnyRoute> =
  RouteByPath<TRouteTree, `${string}/`> extends never ? { to: string } : {};

export type ActiveLinkOptions<TRouteTree extends AnyRoute> = {
  to: string;
} & CheckPath<TRouteTree>;

export type WithComponentOverride<P, T extends React.ElementType> = P & InferProps<T> & {
  component: T;
};

export type InferProps<T extends React.ElementType> =
  T extends React.ComponentType<infer P> ? P : {};

declare const Link2: LinkComponent;
type Orig_TanstackLinkProps = Parameters<typeof Link2>[0];

const Link = <T extends React.ElementType>(
  props: WithComponentOverride<Orig_TanstackLinkProps, T>
) => {
  const p: keyof typeof props = 'component';
  const Component = props.component; // TS2339: Property component does not exist on type <...blabla...>
  // ...
  return <Component {...props} />;
};

export type LinkComponent = <TRouteTree extends AnyRoute>(props: ActiveLinkOptions<TRouteTree>) => React.ReactElement;

We need someone to provide a minimal self-contained repro here; the tanstack types are beyond comprehension.

@RyanCavanaugh RyanCavanaugh added the Needs More Info The issue still hasn't been fully clarified label Apr 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs More Info The issue still hasn't been fully clarified
Projects
None yet
Development

No branches or pull requests

2 participants