programing

React의 레스트 소품용 TypeScript 회피책

topblog 2023. 2. 22. 21:31
반응형

React의 레스트 소품용 TypeScript 회피책

TypeScript 2.1용으로 갱신되었습니다.

TypeScript 2.1은 오브젝트 확산/휴식을 지원하므로 더 이상 회피책이 필요하지 않습니다.


첫 번째 질문

TypeScript는 컴포넌트에서 렌더링된 HTML 요소로 HTML 속성을 전달하기 위해 React에서 일반적으로 사용되는 JSX 확산 속성을 지원합니다.

interface LinkProps extends React.HTMLAttributes {
  textToDisplay: string;
}

class Link extends React.Component<LinkProps, {}> {
  public render():JSX.Element {
    return (
      <a {...this.props}>{this.props.textToDisplay}</a>
    );
  }
}

<Link textToDisplay="Search" href="http://google.com" />

그러나 알 수 없는 소품을 HTML 요소에 전달하면 React에 경고가 추가되었습니다.위의 예에서는 다음과 같은 React 런타임 경고가 생성됩니다.textToDisplay<a>이 예와 같은 경우 오브젝트레스트 속성을 사용하여 커스텀 소품을 추출하고 나머지는 JSX 스프레드 속성으로 사용하는 것이 좋습니다.

const {textToDisplay, ...htmlProps} = this.props;
return (
  <a {...htmlProps}>{textToDisplay}</a>
);

그러나 TypeScript는 아직 이 구문을 지원하지 않습니다. 언젠가는 TypeScript에서 작업을 수행할 수 있을 것으로 생각합니다.(업데이트: TS 2.1은 객체 확산/휴식에 대응하고 있습니다. 왜 아직도 이걸 읽고 있어요??) 그 사이에 회피책에는 어떤 것이 있습니까?나는 타입의 안전성을 해치지 않는 해결책을 찾고 있으며, 그것이 놀라울 정도로 어렵다는 것을 알고 있다.예를 들어 다음과 같이 할 수 있습니다.

const customProps = ["textDoDisplay", "otherCustomProp", "etc"];
const htmlProps:HTMLAttributes = Object.assign({}, this.props);
customProps.forEach(prop => delete htmlProps[prop]);

그러나 실제 소품에 대해 검증되지 않은 문자열 속성 이름을 사용해야 하므로 오타가 발생하거나 IDE 지원이 잘못될 수 있습니다.우리가 이걸 할 수 있는 더 좋은 방법이 없을까?

실제로 위의 모든 답변보다 쉽습니다.다음 예만 따르면 됩니다.

type Props = {
  id: number,
  name: string;
  // All other props
  [x:string]: any;
}

const MyComponent:React.FC<Props> = props => {
  // Any property passed to the component will be accessible here
}

경우 속성 것을 수입니다.this.props을 사용하다

예를 들어 다음과 같습니다.

interface LinkProps {
    textToDisplay: string;
}

const LinkPropsKeys: LinkProps = { textToDisplay: "" };

class Link extends React.Component<LinkProps & React.HTMLAttributes, {}> {
    public render(): JSX.Element {
        return (
            <a { ...this.getHtmlProps() }>{ this.props.textToDisplay }</a>
        );
    }

    private getHtmlProps(): React.HTMLAttributes {
        let htmlProps = {} as React.HTMLAttributes;

        for (let key in this.props) {
            if (!(LinkPropsKeys as any)[key]) {
                htmlProps[key] = this.props[key];
            }
        }

        return htmlProps;
    }
}

「」를 사용합니다.LinkPropsKeys-.LinkProps는 인터페이스와 런타임룩업 사이의 키를 동기화하는 데 도움이 됩니다.

에서 React에 해야 했습니다.Html Attributes html html html html 。React.AnchorHTMLAttributes<HTMLAnchorElement>.

예:

import React from 'react';

type  AClickEvent = React.MouseEvent<HTMLAnchorElement>;

interface LinkPropTypes extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
    to: string;
    onClick?: (x: AClickEvent) => void;
}

class Link extends React.Component<LinkPropTypes> {
  public static defaultProps: LinkPropTypes = {
    to: '',
    onClick: null,
  };

private handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
   ...
    event.preventDefault();
    history.push(this.props.to);
 };

  public render() {
    const { to, children, ...props } = this.props;
    return (
      <a href={to} {...props} onClick={this.handleClick}>
        {children}
      </a>
    );
    }
}

export default Link;

...rest

type ButtonProps = {
    disabled: boolean;
};

function Button(props: ButtonProps): JSX.Element {
    const {disabled = false, ...rest} = props;
...
return (
    <button disabled={disabled} {...rest}>
....

다음과 같은 게터가 작동할 수 있습니다.

class Link extends React.Component<{
  textToDisplay: string;
} & React.HTMLAttributes<HTMLDivElement>> {

  static propTypes = {
    textToDisplay: PropTypes.string;
  }

  private get HtmlProps(): React.HTMLAttributes<HTMLAnchorElement> {
    return Object.fromEntries(
      Object.entries(this.props)
      .filter(([key]) => !Object.keys(Link.propTypes).includes(key))
    );
  }

  public render():JSX.Element {
    return (
      <a {...this.HtmlProps}>
        {this.props.textToDisplay}
      </a>
    );
  }
}

<Link textToDisplay="Search" href="http://google.com" />

[x:string]: any;승인된 답변은 다음과 같습니다.어레이의 구문과 매우 비슷하지만 실제로는 오브젝트의 키 유형을 지정합니다.string그리고 그 가치관이 유형이다.anyTypeScript 용어로는 "Index Signature"라고 합니다.

다만, 대체적이고 덜 느슨한 타입의 솔루션으로서 사용하고 있는 라이브러리에서, 이러한 타입도 export 할 수 있는 경우가 있습니다.

예를 들어 Ant's Buttons를 확장하면 다음과 같이 할 수 있습니다.

import { ReactNode } from "react";
import { Button as AntButton } from "antd";
import { NativeButtonProps } from "antd/lib/button/button";

interface IButtonProps {
  children?: ReactNode;
}

const Button = ({
  children,
  ...rest
}: IButtonProps & NativeButtonProps): JSX.Element => {
  return <AntButton {...rest}>{children}</AntButton>;
};

export default Button;

1: 앰퍼샌드(&) 연산자:IButtonProps & NativeButtonProps는 단순히 TypeScript 내의 타입을 "머지"합니다.Ant Button 소품에 대한 인텔리센스를 잃지 않습니다.버튼을 사용하지 않기 때문입니다.any더이상.Ant Button의 타입과 당신의 IButton Props가 결합되어 있어 둘 다 존재합니다.

메모 2: 또, 이 타입을 어디서 찾았는지 궁금할 수도 있습니다.이 유형은 https://github.com/ant-design/ant-design/blob/master/components/button/button.tsx#L124로 내보냈습니다.또한 인텔리센스를 사용하여 포함 경로를 실현할 수 있습니다.Native Button을 입력하기만 하면 됩니다.당신에게 제안해야 합니다.

니젠 토머의 대답은 내가 원하던 기본적인 생각이었기 때문에 받아들였다.

보다 일반적인 솔루션으로서 저는 다음과 같은 것을 선택하게 되었습니다.

export function rest(object: any, remove: {[key: string]: any}) {
  let rest = Object.assign({}, object);
  Object.keys(remove).forEach(key => delete rest[key]);
  return rest;
}

이렇게 사용할 수 있습니다.

const {a, b, c} = props;
const htmlProps = rest(props, {a, b, c});

TypeScript가 오브젝트 레스트/스프레드를 지원하게 되면, 다음의 모든 용도를 검색할 수 있습니다.rest()를 심플하게 하다const {a, b, c, ...htmlProps} = props.

React.ComponentPropsWithoutRef/React.ComponentPropsWithRef

https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/에서 설명한 바와 같이

interface Props extends React.ComponentPropsWithoutRef<"button"> {
  // ...
}
    
const FancyButton = (props: Props) => {
  const { /**/ , ...rest} = props
      
  // ...
      
  return <button {...rest}>{/**/}</button>
}

를 사용하고 있는 경우는, 을 사용합니다.React.ComponentPropsWithRef대신

데모

이제 TypeScript는 컴포넌트에 인수로 전달하면 ...rest를 무시합니다.내 생각에...rest 인수는 부모 컴포넌트에 의해 자식 컴포넌트에 전달되는 기본 인수이기 때문에 유형 안전은 필요하지 않습니다.예를 들어 redux는 스토어에 대한 정보를 하위 구성요소로 전달하므로...rest 인수는 항상 존재하며 유형 안전 또는 propType이 필요하지 않습니다.

//here's the right solution

interface schema{
  loading: boolean
}
//pass ...rest as argument
export function MyComponent({loading, ...rest}:schema){
  if (loading) return <h2>Loading ...</h2>
  return (
    <div {...rest}>
      <h2>Hello World </h2>
    </div>
}

강한 텍스트

언급URL : https://stackoverflow.com/questions/40032592/typescript-workaround-for-rest-props-in-react

반응형