본문 바로가기

공부기록/Typescript

# 50 타입의 다양한 활용 유니언 타입, ComponentPropsWithoutRef, Button컴포넌트

 

 유니언 타입


 

1. 프로퍼티를 union타입으로 구성

 

mode 속성을 

해당 타입으로만 지정할 수 있게 만듦 

 

2. 옵셔널 프로퍼티

props로 severity를 넣을 수도 있고 

안넣을 수도 있게 구성

 

 

import { type ReactNode } from "react";

type InfoBoxProps = {
  mode: "hint" | "warning";
  severity?: "low" | "medium" | "high";
  children: ReactNode;
};

const InfoBox = ({ children, mode, severity }: InfoBoxProps) => {
  if (mode === "hint") {
    return (
      <aside className="infobox infobox-hint">
        <p>{children}</p>
      </aside>
    );
  }
  return (
    <aside className={`infobox infobox-warning warning--${severity}`}>
      <h2>Warning</h2>
      <p>{children}</p>
    </aside>
  );
};

export default InfoBox;

 

3.  타입을 유니언으로 설정

둘중 하나만 선택이 되며,  

props로 severity를 넣을 수도 있고 

안넣을 수도 있게 구성

 

그렇게하면 객체 구조분해할당은 안되고, 

아래와 같이 상황에 맞춰 props를 구조분해할당을 사용해야 함

 

import { type ReactNode } from "react";

type HintBoxProps = {
  mode: "hint";
  children: ReactNode;
};

type WarningBoxProps = {
  mode: "warning";
  severity: "low" | "medium" | "high";
  children: ReactNode;
};

type InfoBoxProps = HintBoxProps | WarningBoxProps;

const InfoBox = (props: InfoBoxProps) => {
  const { mode, children } = props;

  if (mode === "hint") {
    return (
      <aside className="infobox infobox-hint">
        <p>{children}</p>
      </aside>
    );
  }

  const { severity } = props;
  return (
    <aside className={`infobox infobox-warning warning--${severity}`}>
      <h2>Warning</h2>
      <p>{children}</p>
    </aside>
  );
};

export default InfoBox;

 

 

 

그렇게 되면 mode props로 warning을 주었을 때는 

severity를 하게하고 

mode가 hint일 때는 severity를 주지 않도록 

만들 수 있게 됨 

 

 

ComponentPropsWithoutRef


 

아토믹한 Input 컴포넌트를 재활용 하기 위해

지정한 props이외에도 많은 내장 속성을 활용해 보려고 함

 

 

type InputProps = {
  label: string;
  id: string;
};

const Input = ({ label, id, ...props }: InputProps) => {
  return (
    <p>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" {...props} />
    </p>
  );
};

export default Input;

 

 

그러려면 많은 프롭스들을 받아들여 줘야 하는데, 일일히 다 쓸순 없고 

아래와 같이 하면 된다

 

 

import { ComponentPropsWithoutRef } from "react";

type InputProps = {
  label: string;
  id: string;
} & ComponentPropsWithoutRef<"input">;

const Input = ({ label, id, ...props }: InputProps) => {
  return (
    <p>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" {...props} />
    </p>
  );
};

export default Input;

 

 

 

props가 anchorProps인지 buttonProps인지에 따라 다른 엘리먼트를 반환 해주기

(type predicate)

 

 

방법 1. 


import { type ComponentPropsWithoutRef } from "react";

type ButtonProps = {
  el: "button";
} & ComponentPropsWithoutRef<"button">;

type AnchorProps = {
  el: "anchor";
} & ComponentPropsWithoutRef<"a">;

const Button = (props: ButtonProps | AnchorProps) => {
  if (props.el === "anchor") {
    return <a className="button" {...props}></a>;
  }

  return <button className="button" {...props}></button>;
};

export default Button;

 

 

 

 

 

방법2. 반환타입  props is AnchorProps 타입을 이용 


 

import { type ComponentPropsWithoutRef } from "react";

type ButtonProps = ComponentPropsWithoutRef<"button">;

type AnchorProps = ComponentPropsWithoutRef<"a">;

const isAnchorProps = (
  props: ButtonProps | AnchorProps
): props is AnchorProps => {
  return "href" in props;
};

const Button = (props: ButtonProps | AnchorProps) => {
  if (isAnchorProps(props)) {
    return <a className="button" {...props}></a>;
  }

  return <button className="button" {...props}></button>;
};

export default Button;

 

 

그러나 이렇게 되면 타입스크립트의 타입제한을 받지 못하기 때문에 

안정적이지 않은 이슈가 있을 수 있다

 

 

 

방법3. never를 이용하는 방법


 

button에서 절대 사용해서는 안되는 속성에 제한을 걸어두는 방법

 

 

 

import { type ComponentPropsWithoutRef } from "react";

type ButtonProps = ComponentPropsWithoutRef<"button"> & {
  href?: never;
};

type AnchorProps = ComponentPropsWithoutRef<"a"> & {
  href?: string;
};

 

 

 

이렇게 된다면 같이 넣을 수 없는 속성을 

타입스크립트에게 알려주게 된다.