Tengo un componente de botón. Simplemente le paso solo un accesorio onClick de muchos accesorios opcionales que he definido:
const Button = (props: ButtonProps) => { const handleClick: React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement> = e => { props.onClick(e); } return ( <StyledButton onClick={handleClick}> {props.children} </StyledButton> ); };Entonces lo estoy usando así:
<Button onClick={(e) => { console.log(e); }}>Click me!</Button>Ahora, ¿cómo es posible que, según el error mencionado en cuestión, el objeto no esté definido? Claramente le estoy pasando la función y eso también según la definición de tipo. Entonces, le estoy pasando un objeto. ¡Suficientemente simple!
... onClick?: React.MouseEventHandler<HTMLElement> ...Agregué algunos controles más estrictos en este proyecto recientemente y los más relevantes son:
"strictFunctionTypes": true, "strictNullChecks": true strict:true ya está presente, este error nunca ocurrió.
¿Cuál es el problema aquí?
Actualizar - Tipos agregados
export interface IBaseButtonProps { type?: ButtonType; disabled?: boolean; size?: ButtonSize; block?: boolean; loading?: boolean | { delay?: number }; icon?: string; className?: string; prefixCls?: string; children?: React.ReactNode; } export type AnchorButtonProps = { href: string, target?: string, onClick: React.MouseEventHandler<HTMLElement> } & IBaseButtonProps & Omit<React.AnchorHTMLAttributes<any>, 'type' | 'onClick'>; export type NativeButtonProps = { onClick: React.MouseEventHandler<HTMLElement>, htmlType?: ButtonHTMLType } & IBaseButtonProps & Omit<React.ButtonHTMLAttributes<any>, 'type' | 'onClick'>; export type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps>Notas:
La posible solución es desestructurar los accesorios y agregar el accesorio predeterminado. O usa defaultProps de React. Pero no estoy seguro si debería requerir eso realmente con Typescript.
Con Typescript 3.7+, también puede usar el encadenamiento opcional para invocar el método prop opcional:
const Button = (props: ButtonProps) => { const handleClick: React.MouseEventHandler< HTMLButtonElement | HTMLAnchorElement > = e => { props.onClick?.(e); // works }; };Puede leer más sobre el uso de encadenamiento opcional: https://www.stefanjudis.com/today-i-learned/opcional-chaining-helps-to-avoid-undefined-is-not-a-function-exceptions/
Ahora, ¿cómo es posible que, según el error mencionado en cuestión, el objeto no esté definido? [sic]
El uso de Partial<T> alrededor export type ButtonProps = Partial<AnchorButtonProps & NativeButtonProps> hace que onClick sea opcional. Cuando usamos Partial<T> , todas las propiedades reciben el ? y por lo tanto se vuelven opcionales, lo que significa que todos ellos pueden ser indefinidos.
Hay dos enfoques para una solución: uno es mantener ButtonProps igual con onClick como opcional, y verificar que onClick esté definido antes de llamarlo (arreglo 1); el otro es cambiar ButtonProps para hacer onClick requerido (corrección 2 y 3).
onClick sigue siendo opcional Use los ButtonProps que ya tiene y luego verifique que onClick esté definido antes de llamarlo. Esto es lo que hace antd en el código que vinculaste en los comentarios.
const Button = (props: ButtonProps) => { const handleClick: React.MouseEventHandler< HTMLButtonElement | HTMLAnchorElement > = e => { if (props.onClick) props.onClick(e); // works }; };onClick se vuelve obligatorio Cambie ButtonProps al no aplicar Partial a NativeButtonProps :
type ButtonProps1 = Partial<AnchorButtonProps> & NativeButtonProps; const Button1 = (props: ButtonProps1) => { const handleClick: React.MouseEventHandler< HTMLButtonElement | HTMLAnchorElement > = e => { props.onClick(e); // works }; };onClick también se vuelve obligatorio Defina un tipo RequireKeys , que le permite especificar las claves que no son opcionales.
type RequireKeys<T, TNames extends keyof T> = T & { [P in keyof T]-?: P extends TNames ? T[P] : never }; type ButtonProps2 = RequireKeys<ButtonProps, "onClick">; const Button2 = (props: ButtonProps2) => { const handleClick: React.MouseEventHandler< HTMLButtonElement | HTMLAnchorElement > = e => { props.onClick(e); // works }; }; Las respuestas a Tipos asignados: eliminar el modificador opcional tienen más información sobre cómo RequireKeys<T> .
Solo una respuesta clara
if (props.onClick) props.onClick(e);
si está definiendo una función props y quiere que sea opcional, defínala como,
export type ButtonProps = { function?: () => void; };Explicación: si desea utilizar una función como accesorios, puede haber instancias en las que desee pasar esa función (como accesorios) y puede haber otras instancias en las que no desee pasarla.
por ejemplo,
Código común DONDE llamar al componente
<Home/>, digamos index.ts/index.js
function myfunction(){ //do something alert("hello") } return ( <> <Home myfunction={myfunction}/> //passing prop <Home/> // not passing </> )En JS, home.js
export default function Home({myfunction}) { const const1 = "Hello World" return ( //do something myfunction(); //IMPORTANT line ) }Ahora, es casi equivalente en TS, home.ts
En TS, definimos tipos de todo. Entonces, en ese caso tenemos que definir el tipo de esta función myfunction también, que estamos pasando.
Entonces, para esta función, nos damos cuenta de que,
() (paréntesis vacíos) es suficiente, si hay parámetros, también debemos definir tipos para ellos.void export type HomeProps = { myfunction?: () => void; }; export default function Home({ myfunction }: HomeProps) { const const1 = "Hello World" return ( //do something if (myfunction) myfunction(); //IMPORTANT line ) }Pista: respuesta anterior