본문 바로가기

React

[React] TypeScript

TypeScript

 

JavaScript With Syntax For Types.

TypeScript extends JavaScript by adding types to the language. TypeScript speeds up your development experience by catching errors and providing fixes before you even run your code.

www.typescriptlang.org

  • JS를 기반으로 한 프로그래밍 언어
  • Strongly-typed 언어: 프로그래밍 언어가 작동하기 전에 데이터의 type을 먼저 확인
JS는 type을 신경쓰지 않는다
const plus = (a, b) => a + b;
plus(2, 2);
// 4
plus(2, "hi");
// '2hi'
const user = {
  firstName: "Angela",
  lastName: "Davis",
  role: "Professor",
};
console.log(user.name);
// undefined

 

데이터의 타입을 제어할 수 있을까?

 

 

const plus = (a: number, b: number) => a + b;

설치

처음부터 TypeScript로 프로젝트 시작하기

create-react-app 관련문서

 

Adding TypeScript | Create React App

Note: this feature is available with react-scripts@2.1.0 and higher.

create-react-app.dev

npx create-react-app my-app --template typescript

기존 프로젝트에 TypeScript 적용하기

1. 아래 설치

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

2. 확장자 변경

  • .js -> .tsx (리액트에서 사용할 경우)로 변경
  •  .js -> .ts 리액트가 아닐 경우

3. tsconfig.json 파일 생성 및 수정

npx tsc --init
tsconfig.json 파일 수정
// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
...

4. index.tsx 파일 수정

아래 오류 발생 시 
TS2345: Argument of type 'HTMLElement | null' is not assignable to parameter of type 'Element | DocumentFragment'.
Type 'null' is not assignable to type 'Element | DocumentFragment'.
// index.tsx
const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);

위 과정까지 한 경우 실행이 되는 것을 확인함

5. @types/styled-components 설치

npm i --save-dev @types/styled-components
호환이 되지 않아 설치가 안될 경우, --legacy-peer-deps를 입력하면 무시하고 설치할 수 있다
npm i --save-dev @types/styled-components --save --legacy-peer-deps

@types?

DefinitelyTyped Repository

 

GitHub - DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.

The repository for high quality TypeScript type definitions. - GitHub - DefinitelyTyped/DefinitelyTyped: The repository for high quality TypeScript type definitions.

github.com

유명한 npm package들을 TypeScript가 이해할 수 있도록 TypeScript Definition이 있는 repo

🏋️‍♂️용어: how to TYPE

component를 type한다 = component에 type을 추가한다 = TS한테 뭐가 뭔지 설명한다


PropTypes vs TypeScript

  • PropTypes는 prop이 거기 있는지 없는지 확인해주지만, 코드를 실행한 "후"에 확인 가능
  • TypeScript는 코드 실행 "전"에 오류를 확인해줌

interface

interface는 object shape을 TS에게 설명해준다

playerObj의 object shape을 설명한 간단한 예시
interface PlayerShape {
  name: string;
  age: number;
}

const sayHello = (playerObj: PlayerShape) =>
  `Hello ${playerObj.name}! You are ${playerObj.age}`;

sayHello({ name: "홍엽", age: 10 });
Circle안에 들어가는 object shape을 CircleProps로 type하고,
Circle이 리턴하는 Container의 object shape을 ContainerProps로 type 함
// Circle.tsx
import styled from "styled-components";

interface ContainerProps {
  bgColor: string;
}

const Container = styled.div<ContainerProps>`
  width: 200px;
  height: 200px;
  border-radius: 100px;
  background-color: ${(props) => props.bgColor};
`;

interface CircleProps {
  bgColor: string;
}

function Circle({ bgColor }: CircleProps) {
  return <Container bgColor={bgColor} />;
}

export default Circle;
// App.tsx
import styled from "styled-components";
import Circle from "./Circle";

function App() {
  return (
    <div>
      <Circle bgColor="teal" />
      <Circle bgColor="tomato" />
    </div>
  );
}

export default App;

optional한 값을 주고 싶은 경우 ?를 사용

interface CircleProps {
  bgColor: string;
  borderColor?: string;
}

default한 값을 주고 싶을 경우 ??를 사용

아래의 경우에 borderColor가 없을 경우 borderColor 값은 bgColor 값 입력
function Circle({ bgColor, borderColor }: CircleProps) {
  return <Container bgColor={bgColor} borderColor={borderColor ?? bgColor} />;

최종 예제 코드

// Circle.tsx
import styled from "styled-components";

interface ContainerProps {
  bgColor: string;
  borderColor: string;
}

const Container = styled.div<ContainerProps>`
  width: 200px;
  height: 200px;
  border-radius: 100px;
  background-color: ${(props) => props.bgColor};
  border: 5px solid ${(props) => props.borderColor};
`;

interface CircleProps {
  bgColor: string;
  borderColor?: string;
  text?: string;
}

function Circle({ bgColor, borderColor, text = "default txt" }: CircleProps) {
  return (
    <Container bgColor={bgColor} borderColor={borderColor ?? bgColor}>
      {text}
    </Container>
  );
}

export default Circle;
// App.js
import styled from "styled-components";
import Circle from "./Circle";

function App() {
  return (
    <div>
      <Circle bgColor="teal" borderColor="coral" text="new text" />
      <Circle bgColor="tomato" />
    </div>
  );
}

export default App;

useState

  • TypeScript는 초기값으로 type을 예측
  • 필요한 경우 옆에 타입을 명시함으로 여러 type을 사용할 수 있음
const [counter, setCounter] = useState<number | string>(1);
setCounter("hi");

Form Event

  • 이벤트 타입의 명시 방법
  • 어떤 타입을 사용할지는 구글링해서 익숙해져야 함
  • event의 target을 currentTarget으로 사용하는 것을 확인할 수 있었음
import { useState } from "react";
import styled from "styled-components";

function App() {
  const [value, setValue] = useState("");
  const onChange = (event: React.FormEvent<HTMLInputElement>) => {
    const {
      currentTarget: { value },
    } = event;
    setValue(value);
  };
  const onSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    console.log(`hello ${value}`);
  };
  return (
    <div>
      <form onSubmit={onSubmit}>
        <input
          value={value}
          onChange={onChange}
          type="text"
          placeholder="username"
        />
        <button>Log in</button>
      </form>
    </div>
  );
}

export default App;

theme 적용 방법

npm i --save-dev @types/styled-components 설치 이후

1. styled component 확장

// styled.d.ts
import "styled-components";

// and extend them!
declare module "styled-components" {
  export interface DefaultTheme {
    textColor: string;
    bgColor: string;
  }
}

2. theme.ts 생성

// theme.ts
import { DefaultTheme } from "styled-components/dist/types";

export const lightTheme: DefaultTheme = {
  bgColor: "white",
  textColor: "black",
};

export const darkTheme: DefaultTheme = {
  bgColor: "black",
  textColor: "white",
};

3. index.tsx에서 theme 적용

// index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { ThemeProvider } from "styled-components";
import { lightTheme } from "./theme";

const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <ThemeProvider theme={lightTheme}>
    <App />
  </ThemeProvider>
);

4. App 내에서 theme 값 사용

// App.tsx
import { useState } from "react";
import styled from "styled-components";

const Container = styled.div`
  background-color: ${(props) => props.theme.bgColor};
`;

const H1 = styled.h1`
  color: ${(props) => props.theme.textColor};
`;

function App() {
  return (
    <div>
      <Container>
        <H1>test</H1>
      </Container>
    </div>
  );
}

export default App;