React + Typescript

타입스크립트 기본

Early_Birdy 2021. 4. 13. 01:54
728x90

타입지정과 관련한 아주 기초적인것은 이해한 상태이기에

예전에 코드를 리뷰해보거나 작성하면서 이해가 안갔던 부분들에 대하여 다시 기초부터 공부를 시작한다.

 

1. Interface           

타입스크립트에서는 number, string, boolean 으로 하나의 특정한 타입을 지정시켜줄 수 있는데

객체는? Object로 타입을 지정해주면 끝인가? 

물론 Object로 타입선언을 해주면 되긴한다. 하지만 객체안에 있는 데이터들의 타입을 알아볼 수가 없다.

따라서 객체안의 세부적인 타입도 선언을 해주기위해 인터페이스가 필요하다.

인터페이스는 일종의 "주물 틀" 이라고 생각하면 좋다. 

지점토, 흙, 나무 등등 다양한 데이터재료를 틀에 넣어서 생성한다고 생각하면된다.

interface Person {
  name: string
  age: number
}

Person 이라는 interface를 만들었다. 그럼 우리는 이 인터페이스에 맞게 재료를 넣어 데이터를 생성하면 된다.

 

const jjahoo: Person = {name='JJAHOO' , age=26}
const ford: Person = {name='FORD', age=34}

Person이라는 타입으로 각각 두개의 객체를 생성하였다.

우리는 이 두개의 변수를 일일히 들여다보지 않아도 타입으로 선언된 Person만 보고도 내부에 있는 데이터들의 형식이 무엇인지 바로 알 수가 있다.

 

1-1) 선택 속성

있을 수도 있고 없을 수도 있는 속성이 있을 것이다. 위에 예로는 이름과 나이는 필수지만 추가적으로 필요한 정보가 있을 수 있으니 말이다.

선택 속성은 간단하게 ? 로 표현한다.

interface Person2 {
  name: string
  age: number
  hobby?: string
}

hobby는 필수적이지 않은 속성을 지정해줄때 ?: 라는 식으로 표현해주면 된다.

 

1-2) 익명 인터페이스

굳이 인터페이스 이름을 넣어주지 않고도 인터페이스를 사용할 수 있다.

const y = {
  name: string
  age: number
  hobby?: string
} = {name='Yasuo', age=99}

비구조화 할당을 통해 바로 넣어줘도 된다. 

그치만 인터페이스의 주된 목적은 재사용할 수 있는 대표적인 "틀" 이라고 생각하기에

이런 방식은 바로 함수내에서 사용하는 것 외에는 특별히 사용하지는 않을 듯 하다.

 

1-3)객체의 타입변환(단언)

인터페이스가 있는데 굳....이 이래야 하나 싶지만

const jjahoo: object = {name:'JJAHOO', age:26}

jjahoo.name // err!!!

(<{name:string}>jjahoo).name // 'JJAHOO'

인터페이스가 아닌 object로 선언된 변수에서 name에 접근하려면 당연히 접근이 안된다

그러면 접근을 할 수 있게끔 만들어 줘야하는데 타입변환은 위와 같다. 

허나 이는 일시적으로 속성이 있게 해주는 타입변환이기에 좋은방법은 아닌듯 하다.

 

 

2. 비구조화할당

파이썬과 C#을 해온 나로써는 이는 정말 편하고 좋은 기능이다.

객체에 대한 접근과 할당 자체를 다른 방향으로 아주 편하게 할 수 있다.

const jjahoo: Person = {name:'JJAHOO', age=26 , hobby='soccer'}

const {name,age} = jjahoo
const {name, ...others} = jjahoo
console.log(others) // {age=26, hobby='soccer'}

jjahoo라는 변수의 데이터를 일부만 사용하고 싶을때 이렇게 비구조화 할당을 통해서 하나씩 빼올 수도 있고

앞에 몇개만 빼온다던가, 나머지를 빼온다던가 할 수 있다.

const jjahoo: Person = {name:'JJAHOO', age=26 , hobby='soccer'}
const ford: Person = {name:'FORD', age=77 , hobby='music'}

const all = {...jjahoo, ...ford}

또한 이렇게 여러 객체들을 하나로 모아서 펼칠 수 있는 전개연산자 도 있다.

이뿐 아니라 비구조화할당은 여러 방법이 있다.

참조 : learnjs.vlpt.us/useful/06-destructuring.html

 

06. 비구조화 할당 (구조 분해) 문법 · GitBook

06. 비구조화 할당 (구조분해) 문법 이번에는 1장 섹션 6 에서도 배웠던 비구조화 할당 문법을 잘 활용하는 방법에 대해서 알아보겠습니다. 이전에 배웠던것을 복습해보자면, 비구조화 할당 문법

learnjs.vlpt.us

 

 

3.함수

type strfunction = (string, number) => void

const func1 : strfunction = function(a:string ,b:number) : void {}

이렇게 함수의 타입을 함수시그니처 로 선언해줄 수 있다.

 

3-1) undefined 조심

타입스크립트에서 undefined는 그 자체로도 타입이다.

타입에 대한 계층도를 그려보자면

 

any -- number, boolean, string --------

      |                                                        | -- undefined

      -- object -- interface, class , ... -----

 

이렇게 나타낼 수 있다. 최상위는 any이고 최하위는 undefined이다.

 

그럼 함수에서 undefined를 사용하면 어떤 일이 일어날 수 있는지 봐보자

function getName(obj: Person) {return obj.name}

const a = getName(undefined)

구문오류는 나질 않는다. 왜?

undefined는 Person을 상속하는 자식타입으로 간주되어

' "undefined"에 대하여 name이라는 속성을 찾을 수 없다 ' 라는 오류가 나온다.

그래서 항상 undefined를 조심해야한다.

function getName(obj: Person){
  return obj != undefined ? obj.name : 'this is undefined'
}

이런식으로 "진짜 undefined" 라는 데이터가 들어올 때를 걸러줘야한다.

 

3-2) 함수에도 선택적 매개변수

function getInfo(a: string, b?: number): void{}

이렇게 매개변수도 선택적으로 할 수 있다.

function getInfo(a: string='DEFAULT', b?: number): void{}

여기서 인자 a로 아무것도 주지 않는다면 자동으로 'DEFAULT'가 할당된다.

 

3-3)  변수와 같은 함수

타입스크립트는 일등함수가 있기에 함수형 프로그래밍이다.

일등함수 = 함수와 변수를 구분(차별)하지 않는다

const , function으로 둘다 표현이 가능하다.

const adder = function(a:number, b:number) {return a+b}

//or

const adder = (a:number, b:number) :number => a+b
const tmp = adder(1,2) // 3

이렇게!

 

3-4) 콜백 함수

함수는 또하나의 값으로 표현된다. 이 말은 다른 함수안에 매개변수로 함수가 들어갈 수 있다는 말.

파이썬의 재귀와 같은가?? 

확인해보자

const f = (callback: () => void) :void => callback()

몸통 안에서 인자로 받은 함수를 호출하였다.

더 자세한 예시를 만들어 보자면,

const init = (callback: () => void) : void => {
  console.log('1')
  callback()
  console.log('3')
}

init(() => console.log('2))

// 1
// 2 
// 3

이렇게 된다는 것.

 

3-5) 고차 함수

const add = (a:number) : (number) => number => (b:number): number => a+b
add(1)(2) // 3

이를 더 자세하게 표현해보자면

type NumFunc = (number) => number

const add = (a:number): NumFunc => {
  const _add: NumFunc = (b:number): number => {
    return a + b
  }
}

이렇게 나타낼 수 있다.

!! _add 함수의 관점에서 보면 a는 외부변수이다. 이 외부변수를 사용하는 형태를 Closure이라고 한다

그럼 이 고차함수는 사용할 때 이렇게 사용할 수 있다.

const f: NumFunc = add(1)
const g = f(2) // 3

오우....고차원이다....

 

3-6) 매개변수에도 비구조화 할당

//위에 선언한 Person

const myPerson = ({name,age}: Person): void => {
  console.log(`name = ${name} and age: ${age}`)
}

매개변수도 변수니까 이런식으로 가능하다.

 

3-7) 객체의 속성 이름을 변수로 만들 때

이거 전에 엄청 헷갈렸었는데 드디어 이해가 간다.

const make = (key,value) => ({[key]: value})
console.log(make('name', 'jjahoo')) // { name: 'jjahoo' }

 

이게 타입스크립트에서의 '색인 가능 타입' 이라고 한다.

type KeyType = {
  [key: string]: string
}

 

4. 배열, 튜플

배열은 객체다. 타입은 ' 데이터 타입[] ' .

type Person = {name: string, age?: number}
const arr: Person[] = [ {name: 'JJAHOO'} , {name: 'Jieun', age=23} ]

이렇게 사용가능하다

 

파이썬에서는 문자열은 일종의 배열이어서 a="qwe" 라고 할때, a[0] == q 로 나오는데

타입스크립트에선 그게 안된다 따라서 split을 써야한다. 이는 몇번 써봐서 알고 있다.

 

4-1) 배열의 비구조화 할당

const arr:number[] = [1,2,3,4,5]
const [st,nd,th,...rest] = arr
// st=1  nd=2  th=3  rest=[4,5]

 

 

4-2) 제네릭 방식 타입

number[] 처럼 고정된 타입말고 배열의 데이터들의 타입을 한꺼번에 표현하자! " T[] "

const arrLength = (array: T[]): number => array.length

여기서 T가 타입변수라는 것을 알려줘야 한다. 그래서 다시 코드를 써보면

const arrLength = <T>(array: T[]): number => array.length

이렇게 <T>를 사용하여 알려준다

그러면 사용할때 다양한 배열타입에 대응하게 된다.

const numArr: number[] = [1, 2, 3]
const strArr: string[] = ['a', 'b', 'c', 'd']

arrLength(numArr) // 3
arrLength(strArr) // 4

좋아!