타입스크립트 간단 정리

김재한·2024년 2월 7일
0

서론

타입스크립트에 대해 설명하는 것을 목적으로 하는것이 아닌, 개인적으로 공부한 내용을 정리하기 위해 작성한 글이다. 그렇기때문에 제외된 내용이 있을 수 있고 순서가 맞지 않을 수 있다는 점 양해바란다.

TypeScript

필요성

타입스크립트를 사용하면, 타입을 정의함으로써 에러를 사전에 방지할 수 있고 타입별 관련 함수를 자동완성 시켜주어(IDE 지원) 개발의 생산성을 향상 시킬 수 있다.
이러한 이유들로 대규모 프로젝트에서는 타입스크립트를 필수로 사용하고 있으며 요즘 많은 곳에서도 도입하고있다.

설치

React 에서 TypeScript 를 사용 할 예정이라 CRA로 프로젝트를 생성한다.

npx create-react-app learn-ts --template typescript

권한관련 에러가 발생해 해당 폴더의 소유권을 변경했다

sudo chown -R $USER ./learn-ts

타입의 종류

일반 타입

  • 문자열: string
  • 숫자: number
  • True/False: boolean
  • 배열: string[], number[]
  • 객체: {key1: 타입, key2: 타입}
    • 타입 부분에는 string, number 등을 넣어주면 된다.
    • 특정 키값이 필수가 아닌 경우에는 ?를 붙여 key1?: 타입으로 작성 가능하다.
    • key 값이 여러개인 경우에는 [key: string]: string 으로 작성 가능하다.
      type Member ={[key: string]: string}
       let jae: Member={name:'jae', age: '31'}
  • 빈 값: null, undefined, void(함수형)
  • 모든 값: any

Union Type

둘 이상의 타입을 |로 연결해 or 조건을 갖는 타입이다.

: string | number  // 문자와, 숫자
: (string | number)[] // 문자와 숫자를 요소로 둘 수 있는 배열

Tuple Type

튜플은 배열의 길이와 요소의 타입을 지정한다.

type Member = [number, string]
let kim: Member = [32, 'jae']

Literal Type

변수에 특정 값을 지정해주는 타입이다.

// 변수
let sns: 'kakao' | 'google' // kakao와 google만 허용한다.

//함수
const testFn = (a: 'hello'): 1|0 =>{
  return 0 // 1이나 0만 리턴할 수 있다.
}

< 자주하는 실수>

var person = {name: 'kim'}

const testFn = (name: 'kim'): string =>{
  return `안녕하세요 ${name}`
}

testFn(person.name) // error!!

testFn의 인자인 name의 타입은 'kim' 으로 지정되어 있는데
person.name 은 자료형이 'kim' 이지 타입은 'string' 이므로 에러가 발생한다.
  

기본 문법

변수 및 상수

let 변수: 타입 = 타입에 맞는 값;

let num: number = 0;
let str: string = 'abc';
let arr: string[] = ['a', 'b', 'c']
let obj: {name: string, age: number} = {name: 'jae', age: 29}
...

함수

타입스크립트에서는 함수의 인자를 필수값으로 간주한다. 따라서, 인자의 갯수가 맞지 않으면 에러가 발생한다. 선택적으로 인자를 받기 원하면 ?를 붙여주면 된다.

const 함수명 = (인자1: 타입, 인자2?: 타입): 함수타입=>{
  
}

const testFn = (a: number, b?: number): number =>{
  return a+b;
}

type에 alias 적용

type을 변수로 만들어 사용할 수 있으며, 이 때 첫 글자는 대문자로 시작한다. 타입 변수들을 Union 할 때 사용할 수 있다

type Member = {
    [key :string] : string
} // alias

let kim : Member ={
    name: 'jae',
    age: '31'
}
---------------------------
type Name = string
type Age = number
type Person = Name | Age // 이렇게 Union이 가능하다.

let person :Person = 'kim'

// object의 경우 '&'를 사용해서 Union 해주어야 한다.
type PositionX = {x :number}
type PositionY = {y :number}
type Position = PositionX & PositionY  // {x :number, y :number}

함수의 type에 alias 를 적용하는 방법은 조금 다르다.

type 타입명 = (a?: 타입) => 타입

type FunType = (a?: string) => number

const testFn: FunType = (a) => {
  return 0;
}

testFn('abc') // 0

인터페이스 (Interface)

객체의 타입(object)을 정의할 때 유용하게 사용되며, 상속이나 확장에 있어 장점이 있다.

interface Hashtag {
  tagId?: number,
  title: string,
  count: number
}

let hashTag = {
  title: '인기검색어',
  count: 13
}

const getHashTag = (tag: Hashtag) =>{
  return tag
}

getHashTag(hashTag) // {title: '인기검색어', count: 13}

트위터 클론 코딩에서 사용한 인터페이스 예시를 하나 가져와봤다.

// /src/model/post.ts

interface UserID {
    userId: string,
}

interface PostImage {
  link: string,
  imageId: number,
  Post?: Post
}

export interface Post {
    postId: number;
    User: User;
    content: string;
    createdAt: Date;
    Images: PostImage[],
    Hearts: UserID[],
    Reposts: UserID[],
    Comments: UserID[],
    _count: {
        Hearts: number,
        Reposts: number,
        Comments: number,
    },
    Original?: Post; // 재게시
    Parent?: Post; // 답글
}

💡 Type 과 Interface의 차이

// interface            // type
interface User {   |    type User {
  name: string,    |      name: string,
  age: number      |      age:number
}                  |    }

두 방법 모두 사용 가능하지만, Interface 의 경우 확장이 가능하다는 장점이 있다. 가능하면 type 보다는 Interface를 사용하는 것 이 좋다.

읽기전용 속성 (readonly)

읽기전용으로 선언된 속성은 처음 객체를 생성한 이후 변경이 불가능하다.

interface PostImage {
  link: string,
  Post?: Post,
  readonly  imageId: number,
}

let postImage: PostImage = {
  link: 'https://~~~~~',
  imageId: 1
}
...
postImge.imageId = 3 // error!!

함수에서 사용하기

객체타입 외에도 함수에서도 사용 할 수 있다.

interface login{
  // (인자1: 타입, 인자2: 타입): 리턴 타입
  (username: string, password: string): boolean;
}

const loginUser: login = (username, password) =>{
  
  return true or false
}

loginUser('tester', 5837264) // error!!
loginUser('tester', 'admin11')

제네릭 (Generics)

타입을 함수나 인터페이스의 파라미터로 넘겨 사용하는 문법이다. 즉 사용자가 상황에따라 타입을 정할 수 있다.

// 함수에서 사용
const getSomething = <T>(prop: T): T =>{
	return prop
}

getSomething<string>('hi')
getSomething<number>(30)
getSomething<boolean>(true)

// 인터페이스에서 사용
interface Person<T> {
  name: string,
  age: T
}
const kim: Person<number> ={
  name: 'jae',
  age: 31
}

//타입에 사용
type User<T> ={
  name: string,
  age: T
}
const kim: User<number> = {
  name: 'jae',
  age: 30
}

타입추론

변수를 선언하거나 초기화 할 때 타입이 정해지는 것을 말한다. 실무에서는 모든 변수에 타입을 명시하지 않고, 타입추론을 많이 사용한다.

const [item, setItem] = useState(10)   -> number

API 타입 정의

API 에서 받아오는 응답 데이터의 타입을 정의하는 것이다. async & await 은 Promise 객체이므로 제네릭으로 타입을 선언해준다.

// api 정보
url : https://jsonplaceholder.typicode.com/todos
data:
[
  {
    "userId": 1,
    "id": 1,
    "title": "delectus aut autem",
    "completed": false
  },
  {
    "userId": 1,
    "id": 2,
    "title": "quis ut nam facilis et officia qui",
    "completed": false
  },
  {
    "userId": 1,
    "id": 3,
    "title": "fugiat veniam minus",
    "completed": false
  }
]

// script
interface Todo{
    userId: number;
    id: number;
    title: string;
    completed: boolean

  }

  const apiUrl = "https://jsonplaceholder.typicode.com/todos"
  async function fetchTodos(): Promise<Todo[]>{ // 제네릭 타입 선언
    const response = await fetch(apiUrl);
    const data = await response.json();
    return data;
  }

  fetchTodos().then(todoList =>{
    console.log('todo[0]: ', todoList[0]) // delectus aut autem
  })

Chat GPT를 활용한 타입 정의

Chat GPT 질의를 통해 데이터 타입을 빠르게 정의할 수 있다. 위의 예제를 활용해보겠다.


response data를 가지고 GPT에게 물어보면

아주 친절하게 알려준다.

참고
Typescript 핸드북

0개의 댓글