UNPKG

@rbxts/react

Version:
1,455 lines (1,360 loc) • 82.6 kB
import * as PropTypes from "./prop-types"; declare const UNDEFINED_VOID_ONLY: unique symbol; // Destructors are only allowed to return void. type Destructor = () => void | { [UNDEFINED_VOID_ONLY]: never }; type InferNone<T> = T extends undefined ? typeof React.None : T; type MapToNone<T> = { [K in keyof T]-?: InferNone<T[K]> }; export = React; export as namespace React; declare namespace React { // // React Elements // ---------------------------------------------------------------------- /** * Used to retrieve the possible components which accept a given set of props. * * Can be passed no type parameters to get a union of all possible components * and tags. * * Is a superset of {@link ComponentType}. * * @template P The props to match against. If not passed, defaults to any. * @template Tag An optional tag to match against. If not passed, attempts to match against all possible tags. * * @example * * ```tsx * // All components and tags (imagelabel, imagebutton etc.) * // which accept `Image` * type ImageComponents = ElementType<{ Image: string }>; * ``` * * @example * * ```tsx * // All components * type AllComponents = ElementType; * ``` * * @example * * ```tsx * // All custom components which match `Image`, and tags which * // match `Image`, narrowed down to just `imagelabel` and `imagebutton` * type ImageComponents = ElementType<{ Image: string }, "imagelabel" | "imagebutton">; * ``` */ type ElementType<P = any, Tag extends keyof JSX.IntrinsicElements = keyof JSX.IntrinsicElements> = | { [K in Tag]: P extends JSX.IntrinsicElements[K] ? K : never }[Tag] | ComponentType<P>; /** * Represents any user-defined component, either as a function or a class. * * Similar to {@link JSXElementConstructor}, but with extra properties like * {@link FunctionComponent.defaultProps defaultProps } and * {@link ComponentClass.contextTypes contextTypes}. * * @template P The props the component accepts. * * @see {@link ComponentClass} * @see {@link FunctionComponent} */ type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>; /** * Represents any user-defined component, either as a function or a class. * * Similar to {@link ComponentType}, but without extra properties like * {@link FunctionComponent.defaultProps defaultProps } and * {@link ComponentClass.contextTypes contextTypes}. * * @template P The props the component accepts. */ type JSXElementConstructor<P> = | ((props: P, context?: any) => ReactNode) | (new (props: P, context?: any) => Component<any, any>); /** * A readonly ref container where {@link current} cannot be mutated. * * Created by {@link createRef}, or {@link useRef} when passed `null`. * * @template T The type of the ref's value. * * @example * * ```tsx * const ref = createRef<Frame>(); * * ref.current = React.createElement("Frame"); // Error * ``` */ interface RefObject<T> { /** * The current value of the ref. */ readonly current: T | undefined; } /** * A callback fired whenever the ref's value changes. * * @template T The type of the ref's value. * * @see {@link https://react.dev/reference/react-dom/components/common#ref-callback React Docs} * * @example * * ```tsx * <frame ref={(current) => print(current)} /> * ``` */ type RefCallback<T> = (instance: T | undefined) => void; /** * A union type of all possible shapes for React refs. * * @see {@link RefCallback} * @see {@link RefObject} */ type Ref<T> = RefCallback<T> | RefObject<T> | undefined; /** * A legacy implementation of refs where you can pass a string to a ref prop. * * @see {@link https://react.dev/reference/react/Component#refs React Docs} * * @example * * ```tsx * <frame ref="myRef" /> * ``` */ type LegacyRef<T> = string | Ref<T>; /** * Retrieves the type of the "ref" prop for a given component type or tag name. * * @template C The component type. * * @example * * ```tsx * type MyComponentRef = React.ElementRef<typeof MyComponent>; * ``` * * @example * * ```tsx * type FrameRef = React.ElementRef<"frame">; * ``` */ type ElementRef< C extends | ForwardRefExoticComponent<any> | { new (props: any): Component<any> } | ((props: any, context?: any) => ReactNode) | keyof JSX.IntrinsicElements, > = // need to check first if `ref` is a valid prop for ts@3.0 // otherwise it will infer `{}` instead of `never` "ref" extends keyof ComponentPropsWithRef<C> ? NonNullable<ComponentPropsWithRef<C>["ref"]> extends Ref<infer Instance> ? Instance : never : never; type ComponentState = any; /** * A value which uniquely identifies a node among items in an array. * * @see {@link https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key React Docs} */ type Key = string | number; /** * @internal The props any component can receive. * You don't have to add this type. All components automatically accept these props. * ```tsx * const Component = () => <frame />; * <Component key="one" /> * ``` * * WARNING: The implementation of a component will never have access to these attributes. * The following example would be incorrect usage because {@link Component} would never have access to `key`: * ```tsx * const Component = (props: React.Attributes) => props.key; * ``` */ interface Attributes { key?: Key | undefined; children?: ReactNode; } /** * The props any component accepting refs can receive. * Class components, built-in components (e.g. `frame`) and forwardRef components can receive refs and automatically accept these props. * ```tsx * const Component = forwardRef(() => <frame />); * <Component ref={(current) => print(current)} /> * ``` * * You only need this type if you manually author the types of props that need to be compatible with legacy refs. * ```tsx * interface Props extends React.RefAttributes<Frame> {} * declare const Component: React.FunctionComponent<Props>; * ``` * * Otherwise it's simpler to directly use {@link Ref} since you can safely use the * props type to describe to props that a consumer can pass to the component * as well as describing the props the implementation of a component "sees". * {@link RefAttributes} is generally not safe to describe both consumer and seen props. * * ```tsx * interface Props extends { * ref?: React.Ref<Frame> | undefined; * } * declare const Component: React.FunctionComponent<Props>; * ``` * * WARNING: The implementation of a component will not have access to the same type in versions of React supporting string refs. * The following example would be incorrect usage because {@link Component} would never have access to a `ref` with type `string` * ```tsx * const Component = (props: React.RefAttributes) => props.ref; * ``` */ interface RefAttributes<T> extends Attributes { /** * Allows getting a ref to the component instance. * Once the component unmounts, React will set `ref.current` to `undefined` * (or call the ref with `undefined` if you passed a callback ref). * * @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} */ ref?: Ref<T> | undefined; } /** * Represents the built-in attributes available to class components. */ interface ClassAttributes<T> extends Attributes { /** * Allows getting a ref to the component instance. * Once the component unmounts, React will set `ref.current` to `undefined` * (or call the ref with `undefined` if you passed a callback ref). * * @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs} */ ref?: LegacyRef<T> | undefined; } export type Element< P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>, > = ReactElement<P, T>; /** * Represents a JSX element. * * Where {@link ReactNode} represents everything that can be rendered, `ReactElement` * only represents JSX. * * @template P The type of the props object * @template T The type of the component or tag * * @example * * ```tsx * const element: ReactElement = <frame />; * ``` */ interface ReactElement<P = any, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> { type: T; props: P; key: string | undefined; } interface ReactComponentElement< T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>, P = Pick<ComponentProps<T>, Exclude<keyof ComponentProps<T>, "key" | "ref">>, > extends ReactElement<P, Exclude<T, number>> {} interface FunctionComponentElement<P> extends ReactElement<P, FunctionComponent<P>> { ref?: ("ref" extends keyof P ? (P extends { ref?: infer R | undefined } ? R : never) : never) | undefined; } type CElement<P, T extends Component<P, ComponentState>> = ComponentElement<P, T>; interface ComponentElement<P, T extends Component<P, ComponentState>> extends ReactElement<P, ComponentClass<P>> { ref?: LegacyRef<T> | undefined; } type ClassicElement<P> = CElement<P, ClassicComponent<P, ComponentState>>; interface ReactPortal extends ReactElement { children: ReactNode; } // // React Nodes // ---------------------------------------------------------------------- type ReactChild = ReactElement; type ReactFragment = | Map<Key, ReactNode> | ReadonlyMap<Key, ReactNode> | { readonly [key: Key]: ReactNode } | readonly ReactNode[]; type ReactNode = ReactElement | ReactFragment | ReactPortal | boolean | undefined; // // Top Level API // ---------------------------------------------------------------------- // Custom components function createElement<P extends {}>( type: FunctionComponent<P>, props?: (Attributes & P) | undefined, ...children: ReactNode[] ): FunctionComponentElement<P>; function createElement<P extends {}>( type: ClassType<P, ClassicComponent<P, ComponentState>, ClassicComponentClass<P>>, props?: (ClassAttributes<ClassicComponent<P, ComponentState>> & P) | undefined, ...children: ReactNode[] ): CElement<P, ClassicComponent<P, ComponentState>>; function createElement<P extends {}, T extends Component<P, ComponentState>, C extends ComponentClass<P>>( type: ClassType<P, T, C>, props?: (ClassAttributes<T> & P) | undefined, ...children: ReactNode[] ): CElement<P, T>; function createElement<P extends {}>( type: FunctionComponent<P> | ComponentClass<P> | string, props?: (Attributes & P) | undefined, ...children: ReactNode[] ): ReactElement<P>; // Custom components function cloneElement<P>( element: FunctionComponentElement<P>, props?: Partial<P> & Attributes, ...children: ReactNode[] ): FunctionComponentElement<P>; function cloneElement<P, T extends Component<P, ComponentState>>( element: CElement<P, T>, props?: Partial<P> & ClassAttributes<T>, ...children: ReactNode[] ): CElement<P, T>; function cloneElement<P>( element: ReactElement<P>, props?: Partial<P> & Attributes, ...children: ReactNode[] ): ReactElement<P>; /** * Describes the props accepted by a Context {@link Provider}. * * @template T The type of the value the context provides. */ interface ProviderProps<T> { value: T; children?: ReactNode | undefined; } /** * Describes the props accepted by a Context {@link Consumer}. * * @template T The type of the value the context provides. */ interface ConsumerProps<T> { children: (value: T) => ReactNode; } /** * An object masquerading as a component. These are created by functions * like {@link forwardRef}, {@link memo}, and {@link createContext}. * * In order to make TypeScript work, we pretend that they are normal * components. * * But they are, in fact, not callable - instead, they are objects which * are treated specially by the renderer. * * @template P The props the component accepts. */ interface ExoticComponent<P = {}> { (props: P): ReactNode; readonly $$typeof: symbol; } /** * An {@link ExoticComponent} with a `displayName` property applied to it. * * @template P The props the component accepts. */ interface NamedExoticComponent<P = {}> extends ExoticComponent<P> { /** * Used in debugging messages. You might want to set it * explicitly if you want to display a different name for * debugging purposes. * * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} */ displayName?: string | undefined; } /** * An {@link ExoticComponent} with a `propTypes` property applied to it. * * @template P The props the component accepts. */ interface ProviderExoticComponent<P> extends ExoticComponent<P> { propTypes?: WeakValidationMap<P> | undefined; } /** * Used to retrieve the type of a context object from a {@link Context}. * * @template C The context object. * * @example * * ```tsx * import { createContext } from "@rbxts/react"; * * const MyContext = createContext({ foo: "bar" }); * * type ContextType = ContextType<typeof MyContext>; * // ContextType = { foo: string } * ``` */ type ContextType<C extends Context<any>> = C extends Context<infer T> ? T : never; /** * Wraps your components to specify the value of this context for all components inside. * * @see {@link https://react.dev/reference/react/createContext#provider React Docs} * * @example * * ```tsx * import React, { createContext } from "@rbxts/react"; * * const ThemeContext = createContext("light"); * * function App() { * return ( * <ThemeContext.Provider value="dark"> * <ThemeFrame /> * </ThemeContext.Provider> * ); * } * ``` */ type Provider<T> = ProviderExoticComponent<ProviderProps<T>>; /** * The old way to read context, before {@link useContext} existed. * * @see {@link https://react.dev/reference/react/createContext#consumer React Docs} * * @example * * ```tsx * import React, { createContext } from "@rbxts/react"; * * const ThemeContext = createContext("light"); * * function ThemeFrame() { * const theme = useContext(ThemeContext); * return <frame BackgroundColor3={theme === "light" ? Color3.fromRGB(255, 255, 255) : Color3.fromRGB(18, 18, 18)} />; * } * ``` */ type Consumer<T> = ExoticComponent<ConsumerProps<T>>; /** * Context lets components pass information deep down without explicitly * passing props. * * Created from {@link createContext} * * @see {@link https://react.dev/learn/passing-data-deeply-with-context React Docs} * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/context/ React TypeScript Cheatsheet} * * @example * * ```tsx * import { createContext } from "@rbxts/react"; * * const ThemeContext = createContext("light"); * ``` */ interface Context<T> { Provider: Provider<T>; Consumer: Consumer<T>; /** * Used in debugging messages. You might want to set it * explicitly if you want to display a different name for * debugging purposes. * * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} */ displayName?: string | undefined; } /** * Lets you create a {@link Context} that components can provide or read. * * @param defaultValue The value you want the context to have when there is no matching * {@link Provider} in the tree above the component reading the context. This is meant * as a "last resort" fallback. * * @see {@link https://react.dev/reference/react/createContext#reference React Docs} * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/context/ React TypeScript Cheatsheet} * * @example * * ```tsx * import { createContext } from "@rbxts/react"; * * const ThemeContext = createContext("light"); * ``` */ function createContext<T>( // If you thought this should be optional, see // https://github.com/DefinitelyTyped/DefinitelyTyped/pull/24509#issuecomment-382213106 defaultValue: T, ): Context<T>; function isValidElement<P>(object: {} | undefined): object is ReactElement<P>; const Children: ReactChildren; /** * Lets you group elements without a wrapper node. * * @see {@link https://react.dev/reference/react/Fragment React Docs} * * @example * * ```tsx * import React, { Fragment } from "@rbxts/react"; * * <Fragment> * <uilistlayout key="UIListLayout" FillDirection="Horizontal" SortOrder="LayoutOrder" /> * <textlabel key="Text1" Text="Hello" LayoutOrder={1} /> * <textlabel key="Text2" Text="World" LayoutOrder={2} /> * </Fragment> * ``` * * @example * * ```tsx * // Using the <></> shorthand syntax: * * <> * <uilistlayout key="UIListLayout" FillDirection="Horizontal" SortOrder="LayoutOrder" /> * <textlabel key="Text1" Text="Hello" LayoutOrder={1} /> * <textlabel key="Text2" Text="World" LayoutOrder={2} /> * </> * ``` */ const Fragment: ExoticComponent<{ children?: ReactNode | undefined }>; /** * Lets you find common bugs in your components early during development. * * @see {@link https://react.dev/reference/react/StrictMode React Docs} * * @example * * ```tsx * import React, { StrictMode } from "@rbxts/react"; * * <StrictMode> * <App /> * </StrictMode> * ``` */ const StrictMode: ExoticComponent<{ children?: ReactNode | undefined }>; /** * The props accepted by {@link Suspense}. * * @see {@link https://react.dev/reference/react/Suspense React Docs} */ interface SuspenseProps { children?: ReactNode | undefined; // TODO(react18): `fallback?: ReactNode;` /** A fallback react tree to show when a Suspense child (like React.lazy) suspends */ fallback: NonNullable<ReactNode> | undefined; } // TODO(react18): Updated JSDoc to reflect that Suspense works on the server. /** * This feature is not yet available for server-side rendering. * Suspense support will be added in a later release. */ const Suspense: ExoticComponent<SuspenseProps>; /** * The callback passed to {@link ProfilerProps.onRender}. * * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} */ type ProfilerOnRenderCallback = ( /** * The string id prop of the {@link Profiler} tree that has just committed. This lets * you identify which part of the tree was committed if you are using multiple * profilers. * * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} */ id: string, /** * This lets you know whether the tree has just been mounted for the first time * or re-rendered due to a change in props, state, or hooks. * * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} */ phase: "mount" | "update", /** * The number of milliseconds spent rendering the {@link Profiler} and its descendants * for the current update. This indicates how well the subtree makes use of * memoization (e.g. {@link memo} and {@link useMemo}). Ideally this value should decrease * significantly after the initial mount as many of the descendants will only need to * re-render if their specific props change. * * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} */ actualDuration: number, /** * The number of milliseconds estimating how much time it would take to re-render the entire * {@link Profiler} subtree without any optimizations. It is calculated by summing up the most * recent render durations of each component in the tree. This value estimates a worst-case * cost of rendering (e.g. the initial mount or a tree with no memoization). Compare * {@link actualDuration} against it to see if memoization is working. * * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} */ baseDuration: number, /** * A numeric timestamp for when React began rendering the current update. * * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} */ startTime: number, /** * A numeric timestamp for when React committed the current update. This value is shared * between all profilers in a commit, enabling them to be grouped if desirable. * * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} */ commitTime: number, interactions?: Set<SchedulerInteraction>, ) => void; interface ProfilerProps { children?: ReactNode | undefined; id: string; onRender: ProfilerOnRenderCallback; } /** * Lets you measure rendering performance of a React tree programmatically. * * @see {@link https://react.dev/reference/react/Profiler#onrender-callback React Docs} * * @example * * ```tsx * <Profiler id="App" onRender={onRender}> * <App /> * </Profiler> * ``` */ const Profiler: ExoticComponent<ProfilerProps>; // // Component API // ---------------------------------------------------------------------- type ReactInstance = Component<any> | Element; /** * Decorator function that allows extending React Lua Components. */ export function ReactComponent<T extends ComponentClass<any, any>>(ctor: T): T; /** * Decorator function that allows extending React Lua PureComponents. */ export function ReactPureComponent<T extends ComponentClass<any, any>>(ctor: T): T; // Base component for plain JS classes interface Component<P = {}, S = {}, SS = any> extends ComponentLifecycle<P, S, SS> {} class Component<P, S> { /** * If set, `this.context` will be set at runtime to the current value of the given Context. * * @example * * ```ts * type MyContext = number * const Ctx = React.createContext<MyContext>(0) * * class Foo extends React.Component { * static contextType = Ctx * context!: React.ContextType<typeof Ctx> * render () { * return <>My context's value: {this.context}</>; * } * } * ``` * * @see {@link https://react.dev/reference/react/Component#static-contexttype} */ static contextType?: Context<any> | undefined; /** * If using the new style context, re-declare this in your class to be the * `React.ContextType` of your `static contextType`. * Should be used with type annotation or static contextType. * * @example * ```ts * static contextType = MyContext * // For TS pre-3.7: * context!: React.ContextType<typeof MyContext> * // For TS 3.7 and above: * declare context: React.ContextType<typeof MyContext> * ``` * * @see {@link https://react.dev/reference/react/Component#context React Docs} */ // TODO (TypeScript 3.0): unknown context: any; constructor(props: Readonly<P> | P); /** * @deprecated * @see {@link https://legacy.reactjs.org/docs/legacy-context.html React Docs} */ constructor(props: P, context: any); // We MUST keep setState() as a unified signature because it allows proper checking of the method return type. // Setting a field in the state to `React.None` will clear it from the state. This is the only way to remove a field // from a component's state! // See: https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257 // Also, the ` | S` allows intellisense to not be dumbisense setState<K extends keyof S>( state: | ((prevState: Readonly<S>, props: Readonly<P>) => Pick<MapToNone<S>, K> | MapToNone<S> | undefined) | (Pick<MapToNone<S>, K> | MapToNone<S> | undefined), callback?: () => void, ): void; forceUpdate(callback?: () => void): void; render(): ReactNode; // React.Props<T> is now deprecated, which means that the `children` // property is not available on `P` by default, even though you can // always pass children as variadic arguments to `createElement`. // In the future, if we can define its call signature conditionally // on the existence of `children` in `P`, then we should remove this. readonly props: Readonly<P> & Readonly<{ children?: ReactNode | undefined }>; state: Readonly<S>; /** * @deprecated * * @see {@link https://legacy.reactjs.org/docs/refs-and-the-dom.html#legacy-api-string-refs Legacy React Docs} */ refs: { [key: string]: ReactInstance; }; } class PureComponent<P = {}, S = {}, SS = any> extends Component<P, S, SS> {} interface ClassicComponent<P = {}, S = {}> extends Component<P, S> { replaceState(nextState: S, callback?: () => void): void; isMounted(): boolean; getInitialState?(): S; } interface ChildContextProvider<CC> { getChildContext(): CC; } // // Class Interfaces // ---------------------------------------------------------------------- /** * Represents the type of a function component. Can optionally * receive a type argument that represents the props the component * receives. * * @template P The props the component accepts. * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components React TypeScript Cheatsheet} * @alias for {@link FunctionComponent} * * @example * * ```tsx * // With props: * type Props = { name: string } * * const MyComponent: FC<Props> = (props) => { * return <textlabel Text={props.name} /> * } * ``` * * @example * * ```tsx * // Without props: * const MyComponentWithoutProps: FC = () => { * return <frame>MyComponentWithoutProps</frame> * } * ``` */ type FC<P = {}> = FunctionComponent<P>; /** * Represents the type of a function component. Can optionally * receive a type argument that represents the props the component * accepts. * * @template P The props the component accepts. * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/function_components React TypeScript Cheatsheet} * * @example * * ```tsx * // With props: * type Props = { name: string } * * const MyComponent: FunctionComponent<Props> = (props) => { * return <textlabel Text={props.name} /> * } * ``` * * @example * * ```tsx * // Without props: * const MyComponentWithoutProps: FunctionComponent = () => { * return <frame>MyComponentWithoutProps</frame> * } * ``` */ interface FunctionComponent<P = {}> { (props: P, context?: any): ReactNode; /** * Used to declare the types of the props accepted by the * component. These types will be checked during rendering * and in development only. * * We recommend using TypeScript instead of checking prop * types at runtime. * * @see {@link https://react.dev/reference/react/Component#static-proptypes React Docs} */ propTypes?: WeakValidationMap<P> | undefined; /** * Lets you specify which legacy context is consumed by * this component. * * @see {@link https://legacy.reactjs.org/docs/legacy-context.html Legacy React Docs} */ contextTypes?: ValidationMap<any> | undefined; /** * Used to define default values for the props accepted by * the component. * * @see {@link https://react.dev/reference/react/Component#static-defaultprops React Docs} * * @example * * ```tsx * type Props = { name?: string } * * const MyComponent: FC<Props> = (props) => { * return <textlabel Text={props.name} /> * } * * MyComponent.defaultProps = { * name: "John Doe" * } * ``` * * @deprecated Use {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#default_value|default values for destructuring assignments instead}. */ defaultProps?: Partial<P> | undefined; /** * Used in debugging messages. You might want to set it * explicitly if you want to display a different name for * debugging purposes. * * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} * * @example * * ```tsx * * const MyComponent: FC = () => { * return <textlabel Text="Hello!" /> * } * * MyComponent.displayName = "MyAwesomeComponent" * ``` */ displayName?: string | undefined; } type VFC<P = {}> = VoidFunctionComponent<P>; interface VoidFunctionComponent<P = {}> { (props: P, context?: any): ReactNode; propTypes?: WeakValidationMap<P> | undefined; contextTypes?: ValidationMap<any> | undefined; defaultProps?: Partial<P> | undefined; displayName?: string | undefined; } /** * The type of the ref received by a {@link ForwardRefRenderFunction}. * * @see {@link ForwardRefRenderFunction} */ type ForwardedRef<T> = ((instance: T | undefined) => void) | MutableRefObject<T | undefined> | undefined; /** * The type of the function passed to {@link forwardRef}. This is considered different * to a normal {@link FunctionComponent} because it receives an additional argument, * * @param props Props passed to the component, if any. * @param ref A ref forwarded to the component of type {@link ForwardedRef}. * * @template T The type of the forwarded ref. * @template P The type of the props the component accepts. * * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forward_and_create_ref/ React TypeScript Cheatsheet} * @see {@link forwardRef} */ interface ForwardRefRenderFunction<T, P = {}> { (props: PropsWithChildren<P>, ref: ForwardedRef<T>): ReactElement; /** * Used in debugging messages. You might want to set it * explicitly if you want to display a different name for * debugging purposes. * * Will show `ForwardRef(${Component.displayName || Component.name})` * in devtools by default, but can be given its own specific name. * * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} */ displayName?: string | undefined; /** * defaultProps are not supported on render functions passed to forwardRef. * * @see {@link https://github.com/microsoft/TypeScript/issues/36826 linked GitHub issue} for context * @see {@link https://react.dev/reference/react/Component#static-defaultprops React Docs} */ defaultProps?: never | undefined; /** * propTypes are not supported on render functions passed to forwardRef. * * @see {@link https://github.com/microsoft/TypeScript/issues/36826 linked GitHub issue} for context * @see {@link https://react.dev/reference/react/Component#static-proptypes React Docs} */ propTypes?: never | undefined; } /** * Represents a component class in React. * * @template P The props the component accepts. * @template S The internal state of the component. */ interface ComponentClass<P = {}, S = ComponentState> extends StaticLifecycle<P, S> { new (props: P, context?: any): Component<P, S>; /** * Used to declare the types of the props accepted by the * component. These types will be checked during rendering * and in development only. * * We recommend using TypeScript instead of checking prop * types at runtime. * * @see {@link https://react.dev/reference/react/Component#static-proptypes React Docs} */ propTypes?: WeakValidationMap<P> | undefined; contextType?: Context<any> | undefined; /** * Lets you specify which legacy context is consumed by * this component. * * @see {@link https://legacy.reactjs.org/docs/legacy-context.html Legacy React Docs} */ contextTypes?: ValidationMap<any> | undefined; childContextTypes?: ValidationMap<any> | undefined; /** * Used to define default values for the props accepted by * the component. * * @see {@link https://react.dev/reference/react/Component#static-defaultprops React Docs} */ defaultProps?: Partial<P> | undefined; /** * Used in debugging messages. You might want to set it * explicitly if you want to display a different name for * debugging purposes. * * @see {@link https://legacy.reactjs.org/docs/react-component.html#displayname Legacy React Docs} */ displayName?: string | undefined; } interface ClassicComponentClass<P = {}> extends ComponentClass<P> { new (props: P, context?: any): ClassicComponent<P, ComponentState>; getDefaultProps?(): P; } /** * Used in {@link createElement} to represent * a class. * * An intersection type is used to infer multiple type parameters from * a single argument, which is useful for many top-level API defs. * See {@link https://github.com/Microsoft/TypeScript/issues/7234 this GitHub issue} * for more info. */ type ClassType<P, T extends Component<P, ComponentState>, C extends ComponentClass<P>> = C & (new (props: P, context?: any) => T); // // Component Specs and Lifecycle // ---------------------------------------------------------------------- // This should actually be something like `Lifecycle<P, S> | DeprecatedLifecycle<P, S>`, // as React will _not_ call the deprecated lifecycle methods if any of the new lifecycle // methods are present. interface ComponentLifecycle<P, S, SS = any> extends NewLifecycle<P, S, SS>, DeprecatedLifecycle<P, S> { /** * Called immediately after a component is mounted. Setting state here will trigger re-rendering. */ componentDidMount?(): void; /** * Called to determine whether the change in props and state should trigger a re-render. * * `Component` always returns true. * `PureComponent` implements a shallow comparison on props and state and returns true if any * props or states have changed. * * If false is returned, {@link Component.render}, `componentWillUpdate` * and `componentDidUpdate` will not be called. */ shouldComponentUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): boolean; /** * Called immediately before a component is destroyed. Perform any necessary cleanup in this method, such as * cancelled network requests, or cleaning up any DOM elements created in `componentDidMount`. */ componentWillUnmount?(): void; /** * Catches exceptions generated in descendant components. Unhandled exceptions will cause * the entire component tree to unmount. */ componentDidCatch?(error: Error, errorInfo: ErrorInfo): void; } // Unfortunately, we have no way of declaring that the component constructor must implement this interface StaticLifecycle<P, S> { getDerivedStateFromProps?: GetDerivedStateFromProps<P, S> | undefined; getDerivedStateFromError?: GetDerivedStateFromError<P, S> | undefined; } type GetDerivedStateFromProps<P, S> = /** * Returns an update to a component's state based on its new props and old state. * * Note: its presence prevents any of the deprecated lifecycle methods from being invoked */ (nextProps: Readonly<P>, prevState: S) => Partial<S> | undefined; type GetDerivedStateFromError<P, S> = /** * This lifecycle is invoked after an error has been thrown by a descendant component. * It receives the error that was thrown as a parameter and should return a value to update state. * * Note: its presence prevents any of the deprecated lifecycle methods from being invoked */ (error: Error) => Partial<S> | undefined; // This should be "infer SS" but can't use it yet interface NewLifecycle<P, S, SS> { /** * Runs before React applies the result of {@link Component.render render} to the document, and * returns an object to be given to {@link componentDidUpdate}. Useful for saving * things such as scroll position before {@link Component.render render} causes changes to it. * * Note: the presence of this method prevents any of the deprecated * lifecycle events from running. */ getSnapshotBeforeUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>): SS | undefined; /** * Called immediately after updating occurs. Not called for the initial render. * * The snapshot is only present if {@link getSnapshotBeforeUpdate} is present and returns non-null. */ componentDidUpdate?(prevProps: Readonly<P>, prevState: Readonly<S>, snapshot?: SS): void; } interface DeprecatedLifecycle<P, S> { /** * Called immediately before mounting occurs, and before {@link Component.render}. * Avoid introducing any side-effects or subscriptions in this method. * * Note: the presence of {@link NewLifecycle.getSnapshotBeforeUpdate getSnapshotBeforeUpdate} * or {@link StaticLifecycle.getDerivedStateFromProps getDerivedStateFromProps} prevents * this from being invoked. * * @deprecated 16.3, use {@link ComponentLifecycle.componentDidMount componentDidMount} or the constructor instead; will stop working in React 17 * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state} * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ componentWillMount?(): void; /** * Called immediately before mounting occurs, and before {@link Component.render}. * Avoid introducing any side-effects or subscriptions in this method. * * This method will not stop working in React 17. * * Note: the presence of {@link NewLifecycle.getSnapshotBeforeUpdate getSnapshotBeforeUpdate} * or {@link StaticLifecycle.getDerivedStateFromProps getDerivedStateFromProps} prevents * this from being invoked. * * @deprecated 16.3, use {@link ComponentLifecycle.componentDidMount componentDidMount} or the constructor instead * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#initializing-state} * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ UNSAFE_componentWillMount?(): void; /** * Called when the component may be receiving new props. * React may call this even if props have not changed, so be sure to compare new and existing * props if you only want to handle changes. * * Calling {@link Component.setState} generally does not trigger this method. * * Note: the presence of {@link NewLifecycle.getSnapshotBeforeUpdate getSnapshotBeforeUpdate} * or {@link StaticLifecycle.getDerivedStateFromProps getDerivedStateFromProps} prevents * this from being invoked. * * @deprecated 16.3, use static {@link StaticLifecycle.getDerivedStateFromProps getDerivedStateFromProps} instead; will stop working in React 17 * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props} * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void; /** * Called when the component may be receiving new props. * React may call this even if props have not changed, so be sure to compare new and existing * props if you only want to handle changes. * * Calling {@link Component.setState} generally does not trigger this method. * * This method will not stop working in React 17. * * Note: the presence of {@link NewLifecycle.getSnapshotBeforeUpdate getSnapshotBeforeUpdate} * or {@link StaticLifecycle.getDerivedStateFromProps getDerivedStateFromProps} prevents * this from being invoked. * * @deprecated 16.3, use static {@link StaticLifecycle.getDerivedStateFromProps getDerivedStateFromProps} instead * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props} * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ UNSAFE_componentWillReceiveProps?(nextProps: Readonly<P>, nextContext: any): void; /** * Called immediately before rendering when new props or state is received. Not called for the initial render. * * Note: You cannot call {@link Component.setState} here. * * Note: the presence of {@link NewLifecycle.getSnapshotBeforeUpdate getSnapshotBeforeUpdate} * or {@link StaticLifecycle.getDerivedStateFromProps getDerivedStateFromProps} prevents * this from being invoked. * * @deprecated 16.3, use getSnapshotBeforeUpdate instead; will stop working in React 17 * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update} * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void; /** * Called immediately before rendering when new props or state is received. Not called for the initial render. * * Note: You cannot call {@link Component.setState} here. * * This method will not stop working in React 17. * * Note: the presence of {@link NewLifecycle.getSnapshotBeforeUpdate getSnapshotBeforeUpdate} * or {@link StaticLifecycle.getDerivedStateFromProps getDerivedStateFromProps} prevents * this from being invoked. * * @deprecated 16.3, use getSnapshotBeforeUpdate instead * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#reading-dom-properties-before-an-update} * @see {@link https://legacy.reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path} */ UNSAFE_componentWillUpdate?(nextProps: Readonly<P>, nextState: Readonly<S>, nextContext: any): void; } interface Mixin<P, S> extends ComponentLifecycle<P, S> { mixins?: Array<Mixin<P, S>> | undefined; statics?: | { [key: string]: any; } | undefined; displayName?: string | undefined; propTypes?: ValidationMap<any> | undefined; contextTypes?: ValidationMap<any> | undefined; childContextTypes?: ValidationMap<any> | undefined; getDefaultProps?(): P; getInitialState?(): S; } interface ComponentSpec<P, S> extends Mixin<P, S> { render(): ReactNode; [propertyName: string]: any; } function createRef<T>(): RefObject<T>; /** * The type of the component returned from {@link forwardRef}. * * @template P The props the component accepts, if any. * * @see {@link ExoticComponent} */ interface ForwardRefExoticComponent<P> extends NamedExoticComponent<P> { /** * @deprecated Use {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#default_value|default values for destructuring assignments instead}. */ defaultProps?: Partial<P> | undefined; propTypes?: WeakValidationMap<P> | undefined; } /** * Lets your component expose a DOM node to a parent component * using a ref. * * @see {@link https://react.dev/reference/react/forwardRef React Docs} * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/forward_and_create_ref/ React TypeScript Cheatsheet} * * @param render See the {@link ForwardRefRenderFunction}. * * @template T The type of the DOM node. * @template P The props the component accepts, if any. * * @example * * ```tsx * export const FancyButton = forwardRef<TextButton>((props, ref) => ( * <textbutton ref={ref}> * {props.children} * </textbutton> * )); * ``` */ function forwardRef<T, P = {}>( render: ForwardRefRenderFunction<T, P>, ): ForwardRefExoticComponent<PropsWithoutRef<P> & RefAttributes<T>>; /** * Omits the "ref" attribute from the given props object. * * @template P The props object type. */ type PropsWithoutRef<P> = // Omit would not be sufficient for this. We'd like to avoid unnecessary mapping and need a distributive conditional to support unions. // see: https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types // https://github.com/Microsoft/TypeScript/issues/28339 P extends any ? ("ref" extends keyof P ? Omit<P, "ref"> : P) : P; /** Ensures that the props do not include string ref, which cannot be forwarded */ type PropsWithRef<P> = // Just "P extends { ref?: infer R }" looks sufficient, but R will infer as {} if P is {}. "ref" extends keyof P ? P extends { ref?: infer R | undefined } ? string extends R ? PropsWithoutRef<P> & { ref?: Exclude<R, string> | undefined } : P : P : P; type PropsWithChildren<P = {}> = P & { children?: ReactNode | undefined }; /** * Used to retrieve the props a component accepts. Can either be passed a string, * indicating a Roblox element (e.g. "frame", "screengui", etc.) or the type of a React * component. * * It's usually better to use {@link ComponentPropsWithRef} or {@link ComponentPropsWithoutRef} * instead of this type, as they let you be explicit about whether or not to include * the `ref` prop. * * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops/ React TypeScript Cheatsheet} * * @example * * ```tsx * // Retrieves the props an "frame" element accepts * type FrameProps = React.ComponentProps<"frame">; * ``` * * @example * * ```tsx * const MyComponent = (props: { foo: number, bar: string }) => <frame />; * * // Retrieves the props "MyComponent" accepts * type MyComponentProps = React.ComponentProps<typeof MyComponent>; * ``` */ type ComponentProps<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> = T extends JSXElementConstructor<infer P> ? P : T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T] : {}; /** * Used to retrieve the props a component accepts with its ref. Can either be * passed a string, indicating a Roblox element (e.g. "frame", "screengui", etc.) or the * type of a React component. * * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops/ React TypeScript Cheatsheet} * * @example * * ```tsx * // Retrieves the props an "frame" element accepts * type FrameProps = React.ComponentPropsWithRef<"frame">; * ``` * * @example * * ```tsx * const MyComponent = (props: { foo: number, bar: string }) => <frame />; * * // Retrieves the props "MyComponent" accepts * type MyComponentPropsWithRef = React.ComponentPropsWithRef<typeof MyComponent>; * ``` */ type ComponentPropsWithRef<T extends ElementType> = T extends new (props: infer P) => Component<any, any> ? PropsWithoutRef<P> & RefAttributes<InstanceType<T>> : PropsWithRef<ComponentProps<T>>; /** * Used to retrieve the props a custom component accepts with its ref. * * Unlike {@link ComponentPropsWithRef}, this only works with custom * components, i.e. components you define yourself. This is to improve * type-checking performance. * * @example * * ```tsx * const MyComponent = (props: { foo: number, bar: string }) => <frame />; * * // Retrieves the props "MyComponent" accepts * type MyComponentPropsWithRef = React.CustomComponentPropsWithRef<typeof MyComponent>; * ``` */ type CustomComponentPropsWithRef<T extends ComponentType> = T extends new (props: infer P) => Component<any, any> ? PropsWithoutRef<P> & RefAttributes<InstanceType<T>> : T extends (props: infer P, context?: any) => ReactNode ? PropsWithRef<P> : never; /** * Used to retrieve the props a component accepts without its ref. Can either be * passed a string, indicating a Roblox element (e.g. "frame", "screengui", etc.) or the * type of a React component. * * @see {@link https://react-typescript-cheatsheet.netlify.app/docs/react-types/componentprops/ React TypeScript Cheatsheet} * * @example * * ```tsx * // Retrieves the props an "Frame" element accepts * type FrameProps = React.ComponentPropsWithoutRef<"Frame">; * ``` * * @example * * ```tsx * const MyComponent = (props: { foo: number, bar: string }) => <frame />; * * // Retrieves the props "MyComponent" accepts * type MyComponentPropsWithoutRef = React.ComponentPropsWithoutRef<typeof MyComponent>; * ``` */ type ComponentPropsWithoutRef<T extends ElementType> = PropsWithoutRef<ComponentProps<T>>; type ComponentRef<T extends ElementType> = T extends NamedExoticComponent<ComponentPropsWithoutRef<T> & RefAttributes<infer Method>> ? Method : ComponentPropsWithRef<T> extends RefAttributes<infer Method> ? Method : never; // will show `Memo(${Component.displayName || Component.name})` in devtools by default, // but can be given its own specific name type MemoExoticComponent<T extends ComponentType<any>> = NamedExoticComponent<CustomComponentPropsWithRef<T>> & { readonly type: T; }; /** * Lets you skip re-rendering a component when its props are unchanged. * * @see {@link https://react.dev/reference/react/memo React Docs} * * @param Component The component to memoize. * @param propsAreEqual A function that will be used to determine if the props have changed. * * @example * * ```tsx * import { memo } from "@rbxts/react"; * * const SomeComponent = memo(function SomeComponent(props: { foo: string }) { * // ... * }); * ``` */ function memo<P extends object>( Component: FunctionComponent<P>, propsAreEqual?: (prevProps: Readonly<PropsWithChildren<P>>, nextProps: Readonly<PropsWithChildren<P>>) => boolean, ): NamedExoticComponent<P>; function memo<T extends ComponentType<any>>( Component: T, propsAreEqual?: (prevProps: Readonly<ComponentProps<T>>, nextProps: Readonly<ComponentProps<T>>) => boolean, ): MemoExoticComponent<T>; interface LazyExoticComponent<T extends ComponentType<any>> extends ExoticComponent<CustomComponentPropsWithRef<T>> { readonly _result: T; } /** * Lets you defer loading a component’s code until it is rendered fo