타입스크립트 in React

채희태·2022년 8월 30일

why 타입스크립트?

동적언어인 자바스크립트는 각종 타입을 난무하며 아무렇게나 쓸 수 있는데, 이는 장점으로 보일 수 있지만 사실은 큰 단점이라고 한다.
에러를 유발할 뿐만 아니라 타입의 자유도로 인해 메모리 공간에도 부담을 준다.
타입스크립트는 자바스크립트가 갖고 있는 이러한 단점을 커버해주는 역할을 한다.
참고로 타입스크립트는 자바스크립트와 다른 언어가 아니다.
자바스크립트의 기능을 모두 쓸 수 있으며 추가적으로 자바스크립트의 단점을 상쇄시킬 수 있는 기능을 쓸 수 있을 뿐이다.
타입스크립트는 정적 언어로서 바벨을 통해 컴파일 하여 자바스크립트로 변환시켜 사용한다.
이러한 변환 과정에서 에러를 잡아내며 이 에러는 매우 자세하게 출력되므로 핸들링 하기 유용하다.
또한 타입이 정해져 있기 때문에 메모리 공간을 절약할 수 있다.


기본 타입

타입스크립트를 통해 let과 const로 변수를 선언할 때 타입을 지정할 수 있고,
지정한 타입과 다른 값이 할당될 경우 에러를 발생한다.

//숫자 타입
const num: number = 1
//문자열 타입
const text: string = 'hello'
//불리언 타입
const done: boolean = true
//배열 타입
const nums: number[] = [1, 2, 3, 4, 5]
const strs: string[] = ["one", "two", "three", "four", "five"]
//유틸리티 타입
const numOrUndefined: number | undefined = undefined;
const strOrNull: str | null = null;
const color: "red" | "blue" | "green" = "red"

함수

파라미터의 타입을 정의하고 파라미터 오른쪽에 리턴값의 타입을 정의할 수 있다.
정해진 인자는 필수값으로 간주된다.
지정한 타입과 다른 값이 할당될 경우 에러를 발생한다.

function sum(a: number, b: number): number {
  return a + b
}
const result1 = sum(5, 10)

function sumOfArr(arr: number[]) {
  return arr.reduce((acc, cur) => acc + cur, 0)
}
const result2 = sumOfArray([1, 2, 3, 4, 5])

//REST 문법 사용하기
function sumOfRest(num: number, ...arr: number[]): number {
  let total = 0
  const total = arr.forEach(ele => total += ele)
  return a + total
}
const result3 = sumOfRest(10, 1, 2, 3, 4, 5)

void를 사용하여 리턴값이 이무것도 없을 때 정의할 수 있다.

const nullOfFunc = (a: number): void => {
  console.log(a)
}
             

call signature을 사용하여 호출 타입과 반환 타입을 미리 정의하여 불러오기 할 수 있다.

//한개의 타입 정의
type Add = (a: number, b: number) => number;
const add: Add = (a, b) => a + b;
//아래와 같다.
type Add = {
  (a: number): number
}
const add: Add = (a, b) => a + b;

//여러개의 타입 정의
type Print = {
  (arr: number[]): void
  (arr: string[]): void
  (arr: boolean[]): void
}
const prind: Print = (arr) => {
  arr.forEach(ele => console.log(ele))
} 

클래스


type alias

type alias를 사용하여 타입을 재활용할 수 있다.
?(유틸리티 타입)를 사용하면 있을 수도 있고 없을 수도 있는 값을 정의할 수 있다.

type Person = {
  name: string,
  age?: number,
}

//객체에서 사용
const person1: Person = {
  name: 'gildong',
  age: 30,
}
const person2: Person = {
  name: 'sunsin',
}

//함수에서 사용
const personMaker1 = (name: string): Person => {
  return {
    name,
  }
}
const personMaker2 = (name: string, age: number): Person => {
  return {
    name,
    age,
  }
}
const person1 = personMaker('gildong')
const person2 = personMaker('sunsin', 20)

다양한 타입

readonly

타입 앞에(왼 쪽에) readonly를 사용하여 특정 타입의 수정을 하지 못하도록 막을 수 있다.

//case1
const nums: readonly number[] = [1, 2, 3, 4, 5]
nums.push(1) //error (수정 불가)

//case2
type Person = {
  readonly name: string,
  readonly age?: number,
}
const personMaker: Person = (name: string) => ({ name })
const result = personMaker('gildong')
result.age = 20 //error (수정 불가)

Tuple

Typle을 사용하여 배열 값의 타입의 순서를 정의할 수 있다.

const arr = [string, number, boolean] = ['gildong', 20, true]

uknown

타입을 모를 때 사용한다.

const name: unknown
if(typeof name === 'number') {
  const b = a + 1
}

void

변수에 undefined와 null만 할당할 수 있다.

const nothing: void = undefined

interface

interface는 사용할 타입을 약속하듯 정의하는 것이다.

interface Person {
  name: string;
  age?: number;
}

interface Student {
  name: string;
  age?: number;
  study: string[]
}
  
const person: Person = {
  name: 'honggildong',
  age: 20,
}
  
const student: Student = {
  name: 'honggildong',
  age: 20,
  study: ['react', 'javascript', 'css']
}

extends를 사용하여 기존의 interface를 확장시킬 수 있다.

interface Student extends Person {
  study: string[]
}

const student: Student  {
  name: 'honggildong',
  age: 20,
  study: ['react', 'javascript', 'css']
}

제네릭

제네릭은 선언 시점이 아닌 생성 시점에 타입을 명시하여 다양한 타입을 생성할 수 있도록 한다.
일반적으로 T를 placeholder로서 사용한다.
T를 placeholder로서 정의하고 호출할 때 그 자리에 실제 타입을 정의한다.

함수에서 사용할 땐 화살표함수가 아닌 함수 선언식을 사용한다.

function getSize<T>(arr: T[]): number {
  return arr.length
}

//밑에서 <>는 생략가능
const result1 = getSize<number>([1, 2, 3])
const result2 = getSize<string>(['one', 'two', 'three'])
const result3 = getSize<boolean>([true, true, false])
const result4 = setSize<object>([{}, {}, {}])

리턴값을 모를 땐 다음과 같이 사용한다.

function getSomething<T>(something: T): T {
  return something
}

const result1 = getSomething<number>(1)
const result2 = getSomething<string>('one')
const result1 = getSomething<boolean>(true)
const result1 = getSomething<object>({})

객체 속성이 없을 때 다음과 같이 extends를 사용해 확장한다.

interface Person {
  name: string;
}
const person: Person = {
  name: 'gildong',
}
function getPerson<T extends { age: 20 }>(data: T): T {
  return data.age
}

call signature로 제네릭을 사용할 수 있다.

type Print = {
  <T>(arr: T[]): void
}
const print: Print = arr => {
  arr.forEach(ele => console.log(ele))
}
print([1, 2, 3, 4, 5])

type Print = {
  <T>(arr: T[]): T
}
const print: Print = arr => arr[0]
print([1, 2, 3, 4, 5])

//파라미터가 두개 일때,
type Print = <T, M>(a: T[], b: M) => T
const print: Print = a => a[0]
print([1, 2, 3], true)

type alians로 사용하기

type Person<T> = {
  name: string;
  age: number;
  info: T;
}
const person: Person<{ job: string, grade: number }> = {
  name: 'gildong',
  age: 20,
  info: {
    job: 'student',
    grade: 4,
  },
}

interface로 사용하기

interface Person<T> {
  name: string;
  age: number;
  info: T;
}
const person: Person<object> = {
  name: 'sunsin',
  age: 30,
  info: {
    job: student,
    grade: 3
  }
}

react hook인 useState도 제네릭으로 생성한다.
useState 값으로 어떤 타입이 들어올지 모르기 때문이다.

const [num, setNum] = useState<number>(1)
const [str, setStr] = useState<string>('one')

클래스


Typescript in React

타입스크립트 설치하기

CRA로 타입스크립트 프로젝트 설치하기
npx create-react-app my-app --template typescript

이미 진행중인 프로젝트에 타입스크립트 설치하기
npm i --save typescript @types/node @types/react @types/react-dom @types/jest

profile
기록, 공부, 활용

0개의 댓글