Skip to content

"Type instantiation is excessively deep and possibly infinite" and tsc crashing #891

Closed
@fl0ydj

Description

@fl0ydj

Describe the bug

First off, thanks for the awesome library!

We have been observing some weird instances of "Type instantiation is excessively deep and possibly infinite". For some files, tsc is even crashing.

Our repo is private so can't share it but we created a couple of custom Fields/components, so maybe you can spot where this is coming from:

  1. We have been using zod as a form validator on Fields
  2. We wrap all forms in a form context provider to avoid prop drilling and are passing in reference from useRef(form) as we had some problems with stability in earlier versions (not sure if this still makes any difference).
Code
import type { MutableRefObject } from "react";
import { createContext, useContext } from "react";
import type { useForm } from "@tanstack/react-form";

export const FormContext: React.Context<MutableRefObject<ReturnType<typeof useForm<any>>> | undefined> =
  createContext<MutableRefObject<ReturnType<typeof useForm<any>>> | undefined>(undefined);

export function useFormContext<V>(): ReturnType<typeof useForm<V>> {
  const form = useContext(FormContext) as MutableRefObject<ReturnType<typeof useForm<V>>> | undefined;
  if (!form) {
    throw new Error("useFormContext must be used within a FormProvider");
  }
  return form.current;
}
  1. We are using custom fields like in the code below
Code
type IsDeepValueString<T, K extends DeepKeys<T>> = DeepValue<T, K> extends string | number | typeof Any
  ? K
  : never;

type StringDeepKeys<T> = {
  [P in DeepKeys<T>]: IsDeepValueString<T, P>;
}[DeepKeys<T>];

export const TextField = <V, N extends DeepKeys<V> & StringDeepKeys<V>>(
  props: {
    name: N;
    label?: string;
    regexPattern?: string;
    rightElement?: ReactNode;
    isPassword?: boolean;
    extraFieldProps?: Omit<FieldOptions<V, N, any>, "name">;
  } & Omit<InputProps, "name" | "label">,
): ReactNode => {
  const form = useFormContext<V>();
  const { name, label, regexPattern, rightElement, isPassword, ...rest } = props;

  return (
    <form.Field {...props.extraFieldProps} name={props.name}>
      {(field) => {
        console.log("ruleAsset", props.name, field.getValue());

        return (
          <FormControl isInvalid={field.state.meta.isTouched && field.state.meta.errors?.length > 0}>
            {props.label && <FormLabel htmlFor={String(props.name)}>{props.label}</FormLabel>}
            <InputGroup>
              <Input
                type={props.isPassword ? "password" : "text"}
                value={field.getValue() === Any ? "∞" : (field.getValue() as string)}
                name={String(field.name)}
                onChange={(e) => {
                  console.log("ruleasset ruleamount change", e.target.value);
                  const regex = new RegExp(props.regexPattern ?? "");
                  console.log("1");

                  if (e.target.value === "" || !props.regexPattern || regex.test(e.target.value)) {
                    console.log("2");

                    //need to cast as we extend string so its not sure it can be assinged properly
                    field.handleChange(e.target.value as DeepValue<V, N>);
                    console.log("2.1");
                  } else if (e.target.value === "∞") {
                    field.handleChange(Any as DeepValue<V, N>);
                  }
                  console.log("3");
                }}
                onBlur={field.handleBlur}
                {...rest}
              />
              {props.rightElement !== undefined && <InputRightElement>{props.rightElement}</InputRightElement>}
            </InputGroup>
            <FormErrorMessage>
              {field.state.meta.isTouched && field.state.meta.errors?.join(", ")}
            </FormErrorMessage>
          </FormControl>
        );
      }}
    </form.Field>
  );
};
  1. We are using some fields where we receive the value from a query. We want to run validation on it and use it with other fields so we decided for the below structure:
Code
export const GasEstimateSimulation = ({
  gasField,
  metaData,
  userCoin,
  approval,
}: {
  gasField: FieldApi<RequestTransactionValues, FormField.Gas, any>;
  metaData: CoinsData | undefined;
  userCoin: UserCoin | undefined;
  approval: Approval<UserRequestType.RequestTransaction>;
}): ReactNode => {
  const currentWorkspace = useCurrentWorkspace();
  const assets = useAssets(currentWorkspace);

  const estimatedGas = useEstimateGas(getEstimateGasArgsFromApproval(approval));
  console.log("Estimate gas error", estimatedGas, getEstimateGasArgsFromApproval(approval));

  useEffect(() => {
    console.log("setting gas", estimatedGas.data);
    if (estimatedGas.data !== gasField.getValue()) {
      gasField.handleChange(estimatedGas.data);
    }
  }, [estimatedGas.data, gasField]);

  return (
    <TransactionFeeCardFromGasResult
      gas={estimatedGas}
      network={getNetworkFromApproval(approval)}
      userCoin={userCoin}
      metaData={metaData}
      // isLoading={!assets.isSuccess || !estimatedGas.isSuccess}
      fontSize="xs"
      // error={estimatedGas.error}
    />
  );
};

Your minimal, reproducible example

Steps to reproduce

See examples above

Expected behavior

Less performance issues with type checking

How often does this bug happen?

None

Screenshots or Videos

No response

Platform

macOs

TanStack Form adapter

None

TanStack Form version

0.26.4

TypeScript version

5.5.4

Additional context

We are using the zod form adaptor at the same version (0.26.4)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions