본문 바로가기
Web development/React.js & Typescript

[React + Typescript] Typing Tips 리액트 타이핑 팁

by 자몬다 2020. 11. 15.

백엔드에서 라이트하게만 타입스크립트를 사용해오다가, 리액트에서 사용하려니 너무 어려웠다.

백엔드에서는 거의 내가 직접 만든 것들에 대해서만 타이핑하면 되었는데, 리액트에서는 리액트 컴포넌트까지 타이핑해야 하다보니 어떤 타입인지 제대로 입력하기가 헬이었다.

결국 처음엔 any로 도배하면서 구현했다가, 하나씩 실마리를 잡을 때마다 다른곳들을 채워가는 중...

 

그래서, 리액트+타입스크립트 쌩초보 입장에서 팁을 공유하고자 작성한다.

유용하다고 생각되는 것을 찾을 때마다 조금씩 보완할 예정이다.

 

💡 나만 몰랐던 이야기(?)

  • RouteComponentProps은 라우트를 통해 렌더링되는 컴포넌트의 기본 prop이다.
    • history, location, match, staticContext?로 구성되어 있다.
  • React.FunctionCompont = React.FC
  • keyof, typeof, as 키워드가 매우 유용했어요.
  • 어떻게 정의하는게 깔끔할지 고민될땐 타입스크립트 핸드북 에서 찾아보고 있어요.

 

Prop

라우트를 통해 렌더링되는 컴포넌트에 라우트 기본 prop을 전달하는 경우 : RouteComponentProps

import {RouteComponentProps} from 'React';

// router
<Route path="/bar" />

// 라우터를 통해 렌더링될 컴포넌트에서는 아래와 같이 route prop을 꺼내 사용할 수 있다.
const BarComponent: React.FC<RouteComponentProps> = ({match}) => {
	// some codes...
}

라우트 prop과 함께 커스텀 prop도 넘기는 경우 : MyProp & RouteComponentProps

import {RouteComponentProps} from 'React';

// container
const BarComponent = () => {
    return <BarComponentView foo={foo} />;
};

// presentational component
interface IProps {
    foo: Foo;
}

const BarComponentView: React.FC<IProps & RouteComponentProps> = ({
    foo,
    history,
}) => {

 

Property

우리는 정해진 타입만 들어올 것을 알고 있지만, ts는 알지 못하는 경우

Property 'name' does not exist on type 'EventTarget'.

type NameTypes = 'a' | 'b' | 'c';
const bar: NameTypes = event.target.name; // error
// 우리는 event.target.name에 'a', 'b', 'c'만 들어온다는것을 알지만
// ts는 모르기 때문에 에러가 난다.

// 그러므로 아래와 같이 as로 명시해주어야 함!
const bar: NameTypes = event.target.name as NameTypes;

 

자주 사용되는 타입들

특정 객체 리터럴의 키값들을 정의하고 싶은 경우

type Animal = {age: number, name: string};
const cat: Animal = {
  age: 11,
  name: 'effy'
}

// 객체의 키 이름을 넘기면 값을 반환하는 함수. 
// ts는 keyName이 cat의 키값인지 모르므로 에러가 난다.
function catAgeOrName(keyName){
  return cat[keyName];
}

// 아래처럼 keyof로 Cat 타입의 키값임을 정의해줄 수 있다.
function catAgeOrName(keyName: keyof Animal){
    return cat[keyName];
}

유동적인 키값 + 정해진 타입의 객체를 만들고 싶은 경우

// 이런 형태들의 타입을 정의하고 싶다...
const cat = {cat: {age: 11, name: 'effy'}}
const dog = {dog: {age: 5, name: 'ben'}}
// ...나이와 이름을 갖는 다양한 동물 정보 객체

// Record를 사용해 해결할 수 있다.
type AnimalInfo = {age: number, name: string};
type AnimalRecord = Record<string, AnimalInfo>;

const cat: AnimalRecord = {cat: {age: 11, name: 'effy'}};
const bird: AnimalRecord = {bird: {age: 12, name: 'rui'}};

특정 문자열들을 타입으로 지정하고 싶은 경우

// animal 인자의 타입을 지정하지 않으면 tiger가 들어올 수도 있어서 불안하다...
function pet(animal){ 
	console.log(`${animal}을 키우고 싶습니다.`);
}

// cat 또는 dog이라는 값만 갖는 타입을 지정하고 싶다.
type Animals = 'Cat' | 'Dog';

// 유니언 문저열 리터럴 타입이 아닌 enum으로 정의해도 됩니다.
export enum Animal {
    Cat = 'Cat',
    Dog = 'Dog',
}

// tip : 코드상에서 오타가 날 수 있어서 걱정이 될 때, enum을 활용하면 좋습니다.
const cat = 'CAT'; // Cat이어야 하는데 오타가 났다.
const cat = Animal.Cat; // 보다 안전한 코드

유니언 문자열 리터럴 타입을 객체의 키값으로 정의하고 싶은 경우

// 이런 형태의 타입을 정의하고 싶다...키값으로는 cat과 dog을 갖도록 하고 싶다.
const animals: AnimalRecord = {
  cat: {age: 11, name: 'effy'}
  dog: {age: 11, name: 'effy'}
}

type Animal = 'cat' | 'dog';
type AnimalInfo = {age: number, name: string};

// Record를 사용하거나
type AnimalRecord = Record<Animal, AnimalInfo>;

// Bar in Foo 형태로 표현할 수도 있습니다.
type AnimalRecord = {[Key in Animal]: AnimalInfo};

댓글