microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
99.1k stars 12.29k forks source link

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

Open tmax22 opened 2 months ago

tmax22 commented 2 months ago

🔎 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 commented 2 months ago

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 commented 2 months ago

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 commented 2 months ago

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.