10일차(8.1~8.3)

타입스크립트를 사용하는 이유

1. IDE 를 더욱 더 적극적으로 활용 (자동완성, 타입확인)

  • 함수가 어떤 파라미터를 필요로 하는지
  • 어떤 값을 반환하는지
  • 리액트 컴포넌트의 경우 해당 컴포넌트를 사용하게 될 때 props에는 무엇을 전달해줘야하는지
  • 컴포넌트 내부에서도 자신의 props에 어떤 값이 있으며, state에 어떤 값이 있는지 알 수 있다
  • 리덕스와 함께 사용하게 될 때에도 스토어 안에 어떤 상태가 들어있는지 바로 조회가 가능

2. 실수방지

  • 함수, 컴포넌트 등의 타입 추론이 되기 때문에 사소한 오타를 만들면 코드를 실행하지 않더라도
    IDE 상에서 바로 알 수 있게 된다
  • 사전에 null 체킹을 하지 않으면 오류를 띄우므로 null 체킹도 확실하게 할 수 있게 된다

tsc 명령어를 입력해서 컴파일


01. 타입스크립트 연습

변수 타입 지정하기

기본 타입


let count: number = 0; 

const message: string = 'hello world'; // 문자열

const done: boolean = true; // 불리언 값

const numbers: number[] = [1, 2, 3]; // 숫자 배열

const messages: string[] = ['hello', 'world']; // 문자열 배열

let mightBeUndefined: string | undefined = undefined; // string 또는 undefined 

let nullableNumber: number | null = null; // number 또는 null 

// 값까지 제한 할 수 있다?
let color: 'red' | 'orange' | 'yellow' = 'red'; // red, orange, yellow 중 하나

함수 타입


// 일반 함수
             // 파라미터 타입         // 반환 값 타입
function sum(x: number, y: number): number {
  return x + y;
}

sum(1, 2);

// 화살표 함수
let corsURL = (url:string): string => `https://crossorigin.me/${url}`;

타입스크립트를 사용하면 함수실행 코드를 작성할 때, 함수의 파라미터로 어떤 타입을 넣어야 하는지 바로 알 수 있다

배열의 내장함수를 사용 할 때에도 타입 유추가 매우 잘 이루어진다

반환 값이 없다면 반환 타입을 void 로 설정

function returnNothing(): void {
  console.log('I am just saying hello world');
}

객체 타입 지정하기

클래스와 관련된 타입의 경우엔 interface, 일반 객체의 타입의 경우엔 그냥 type을 사용해도 무방하다

객체를 위한 타입을 정의할때 무엇이든 써도 상관 없는데 일관성 있게만 쓰면 된다

1. interface 사용해보기

클래스 또는 객체를 위한 타입을 지정 할 때 사용되는 문법

일반 객체를 interface 로 타입 설정하기

  • interface를 먼저 생성해서 요소들의 타입을 지정한다
  • 실제 객체를 생성할 때에 변수의 type을 작성하는 부분에 만들어놓은 interface의 이름을 적는다
  • 타입을 직접 만드는 것?
interface Person {
  name: string;
  age?: number; // 물음표가 들어갔다는 것은, 설정을 해도 되고 안해도 되는 값이라는 것을 의미합니다.
}
interface Developer {
  name: string;
  age?: number;
  skills: string[];
}

const person: Person = {
  name: '김사람',
  age: 20
};

const expert: Developer = {
  name: '김개발',
  skills: ['javascript', 'react']
};

interface를 선언 할 때 extends 키워드를 사용하면 다른 interface를 상속받아 사용할 수 있다

interface Person {
  name: string;
  age?: number; // 물음표가 들어갔다는 것은, 설정을 해도 되고 안해도 되는 값이라는 것을 의미합니다.
}
// 오버라이딩x
interface Developer extends Person { // Person의 설정을 가져가는~
  skills: string[];
}

const person: Person = {
  name: '김사람',
  age: 20
};

const expert: Developer = {
  name: '김개발',
  skills: ['javascript', 'react']
};

const people: Person[] = [person, expert]; // 요소가 Person객체 type인 배열? 뭐지이게

2. Type Alias 사용하기

  • type 은 특정 타입에 별칭을 붙이는 용도로 사용
  • 그 어떤 타입이던 별칭을 지어줄 수 있다
type Person = {
  name: string;
  age?: number; // 물음표가 들어갔다는 것은, 설정을 해도 되고 안해도 되는 값이라는 것을 의미합니다.
};

// & 는 Intersection 으로서 두개 이상의 타입들을 합쳐줍니다.
// extend와 같은 역할
// &는 a도 b도 존재
// 오버라이딩o
type Developer = Person & {
  skills: string[];
};

const person: Person = {
  name: '김사람'
};

const expert: Developer = {
  name: '김개발',
  skills: ['javascript', 'react']
};

type People = Person[]; // Person[] 를 이제 앞으로 People 이라는 타입으로 사용 할 수 있습니다.
const people: People = [person, expert];

type Color = 'red' | 'orange' | 'yellow';
const color: Color = 'red';
const colors: Color[] = ['red', 'orange'];

Generics

여러 종류의 타입에 대하여 호환을 맞춰야 하는 상황에서 사용하는 문법

함수에서 Generics 사용하기

  • any 라는 타입을 쓸 수도 있지만, 타입 유추가 모두 깨진거나 다름이 없다
  • any대신 Generics을 사용해야 파라미터로 다양한 타입을 넣을 수도 있고 타입 지원을 지켜낼 수 있다
// 함수 이름 옆에 <>를 쓰고 안에 임의의 타입을 설정해서 사용한다
function merge<A, B>(a: A, b: B): A & B {
  return {
    ...a,
    ...b
  };
}

const merged = merge({ foo: 1 }, { bar: 1 });

iterface / type에서 Generics 사용하기

  • 매개변수와 비슷하게 사용
  • 사용 시에 사용할 type을 정한다
interface Items<T, N> { // <>안에 임의의 타입을 설정한다
  list: T[]; // 필요한 곳에 적용한다
}

const items: Items<string, number> = { // <>안에 사용할 타입을 적는다
  list: ['a', 'b', 'c']
};

---

type Items<T> = {
  list: T[];
};

const items: Items<string> = {
  list: ['a', 'b', 'c']
};

02. 리액트 컴포넌트 타입스크립트로 작성하기

뒤에 --typescript 가 있으면 타입스크립트 설정이 적용된 프로젝트가 생성된다

npx create-react-app ts-react-tutorial --typescript

  • TypeScript 를 사용 시 렌더링 할 때 필요한 props 를 빠뜨리게 된다면 에디터에 오류가 나타남
  • 컴포넌트를 사용하는 과정에서 어떤 props들이 필요했는지 알고싶을 때
  • 컴포넌트에 커서를 올린다
  • props를 설정하는 부분에서 Ctrl + Space를 누른다

props객체의 타입을 지정해준다

type GreetingsProps = {
  name: string;
  mark: string;
  optional?: string; // 있어도,없어도 되는 props에는 옵셔널을 붙여준다
  onClick: (name: string) => void; // 함수일 경우의 타입 지정 방법(파라미터의 타입, 반환 값의 타입)
};
import React from 'react';
import logo from './logo.svg';
import './App.css';

// 화살표 함수로 지정 + React.FC라는 type사용
const App: React.FC = () => {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
import React from 'react';

type GreetingsProps = {
  name: string;
  //children지정하지 않아도 자동으로 들어가 있음
};

// 파라미터(props)의 타입을 Generics로 넣어서 사용
const Greetings: React.FC<GreetingsProps> = ({ name }) => (
  <div>Hello, {name}</div>
);

export default Greetings;

React.FC 를 사용 할 때의 이점

  1. props 에 기본적으로 children 이 들어가있다

  1. 컴포넌트의 defaultProps, propTypes, contextTypes 를 설정 할 때 자동완성이 될 수 있다

단점

  • children 이 옵셔널 형태로 들어가있다보니, 컴포넌트의 타입 지정이 완벽하지 않은 문제가 있다 → 결국에는 따로 타입을 지정해야 함
    type GreetingsProps = {
      name: string;
      children: React.ReactNode;
    };
  • 아직까지는 defaultProps 가 제대로 작동하지 않는다

React.FC 를 사용하지 않았을 경우

회살표 함수

import React from 'react';

type GreetingsProps = {
  name: string;
  mark: string;
};

const Greetings = ({ name, mark }: GreetingsProps) => (
  <div>
    Hello, {name} {mark}
  </div>
);

Greetings.defaultProps = {
  mark: '!'
};

export default Greetings;

일반함수

import React from 'react';

type GreetingsProps = {
  name: string;
  mark: string;
};

function Greetings({ name, mark }: GreetingsProps) {
  return (
    <div>
      Hello, {name} {mark}
    </div>
  );
}

Greetings.defaultProps = {
  mark: '!'
};

export default Greetings;

질문

컴포넌트일 경우 반환 값의 타입을 지정하지 않는건가?


03. 타입스크립트로 리액트 상태 관리하기

useState에서 사용하기

useState를 사용 할 때 Generics 를 사용하지 않아도 알아서 타입을 유추하기 때문에 생략해도 상관없지만

상태가 null일 수도 있고 아닐수도 있을때 Generics 를 활용하면 좋다

type Information = { name: string; description: string };
const [info, setInformation] = useState<Information | null>(null);

// state가 Information이라는 객체 타입일수도, null일 수도 있다

질문

const [count, setCount] = useState<number>(0); // 그냥 number타입을 쓰면 되는 거 아닌가?

인풋 상태 관리하기

이벤트를 다룰 때의 타입 지정 방법

event객체의 타입은 ? 커서를 onChange 에 올리면 나온다

import React, { useState } from 'react';

// props객체의 타입을 지정한다
type MyFormProps = {
  onSubmit: (form: { name: string; description: string }) => void;
};

function MyForm({ onSubmit }: MyFormProps) { //props을 꺼내 쓸 때 만들어둔 props타입 이름을 적는다
  const [form, setForm] = useState({
    name: '',
    description: ''
  });

  const { name, description } = form;

  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {

  };

  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
    
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" value={name} onChange={onChange} />
      <input name="description" value={description} onChange={onChange} />
      <button type="submit">등록</button>
    </form>
  );
}

export default MyForm;

useReducer 사용해보기

import React, { useReducer } from 'react';

// 액션 값 자체를 전부 타입으로 지정해놓는다?
type Action = { type: 'INCREASE' } | { type: 'DECREASE' }; 

// type외에 다른 값도 있는 경우 -> type을 사용자가 지정할 수도 있다?
type Action =
  | { type: 'SET_COUNT'; count: number }
  | { type: 'SET_TEXT'; text: string }
  | { type: 'SET_COLOR'; color: Color }
  | { type: 'TOGGLE_GOOD' };

function reducer(state: number, action: Action): number {
  switch (action.type) {
    case 'INCREASE':
      return state + 1;
    case 'DECREASE':
      return state - 1;
    default:
      throw new Error('Unhandled action');
  }
}

function Counter() {
  const [count, dispatch] = useReducer(reducer, 0);
  const onIncrease = () => dispatch({ type: 'INCREASE' });
  const onDecrease = () => dispatch({ type: 'DECREASE' });

  return (
    <div>
      <h1>{count}</h1>
      <div>
        <button onClick={onIncrease}>+1</button>
        <button onClick={onDecrease}>-1</button>
      </div>
    </div>
  );
}

export default Counter;

출처 https://react.vlpt.us/

좋은 웹페이지 즐겨찾기