본문 바로가기

웹개발/타입스크립트

01. Type

최대한 간단 명료하게 설명하며, 일부 심플한 내용을 이해하기 위한 반복적인 상세한 설명은 하지 않는다.(시간낭비)

⇒ 1번 완독하신분들이 복습하기 위한 용도.

 

2012 MS 에서 발표한, JS 기반의 정적 타입 문법을 추가한 언어

→ 정적 타입의 컴파일 언어

타입스크립트의 구성

 

 

타입 종류

  • 문자, 숫자, Boolean
let str: string = 'hello world'
let num: number = 123
let bool: boolean = true

 

  • 객체 타입의 기본 형태
// 파라미터가 필요하면, 파라미터 필수로 넣어야 함.
// 파라미터가 있다면, 기본값도 필수로 넣어야 함.
const obj: { a: number } = { a: 0 }
obj.a = 123

 

  • 배열

배열의 기본은 튜플 타입 배열의 요소타입을 정해줘야 함. 2가지 형태의 배열 선언 방식이 있음.

// 선언 방식 1
const arr: number[] = []
arr[0] = 123
arr[1] = 456
arr.push(789)

// 선언 방식 2
const arr2: Array<string> = []
arr2.push('messi')

 

  • 함수

JS 처럼, 변수에 함수를 할당가능.

// 파라미터 타입과 반환값 타입을 지정
const hello: (msg: string) => string = (msg) => {
  return msg
}

// 바로 return 이면, 화살표
const hello2: (msg: string) => string = (msg) => msg

// 일반 함수 버전
const hello3: (a: string, b: number) => string = function (msg, xyz) {
  // ...
  return msg
}

// 매개변수에 직접 타입 할당하는 버전
const hello4 = function (msg: string, xyz: number): string {
  // ...
  return msg
}

 

  • Enum 타입
enum Week {
  Sun,
  Mon,
  Tue,
  Wed,
  Thu,
  Fri,
  Sat,
}
console.log(Week[0]) // 값 추출
console.log(Week.Sun) // 인덱스 추출

 

  • void 타입 : 값을 반환하지 않는 함수의 반환 타입

undefined 가 아니고 void 를 반환 해야 한다. 따로 return 키워드를 사용하는게 아니면, => void를 반환한다.

const empty1: (msg: string) => void = (msg) => {
  console.log('Hello ${msg}')
}
empty1('World!')

 

  • 튜플 타입 : 고정된 길의 배열
const tup: [number, number] = [4, 5]

// 튜플 사용 예시 : 데이터 유형 고정화
const userA: [number, string, boolean] = [1, 'juno', true]
const userB: [number, string, boolean] = [2, 'evan', false]
const userC: [number, string, boolean] = [3, 'call', masury@gmail.com] // 이렇게 사용하면 안된다는 걸 의미

튜플도 마찬가지로 배열이기 때문에, splice 와 push 같은 배열에 변동을 일으키는 기능을 사용할수 잇는데, 이러한 코드는 런타임에서도 잡지 못하기 때문에 사용하지 말것을 당부한다.

 

  • Never 타입

어떤 것도 할당할 수 없는 타입, 혹은 정상적으로 종료되지 않는 함수의 => 반환 타입.

const myError: (m: string) => never = (msg) => {
	throw '에러 - ${msg}'
}
try {
	myError('never 타입')
} catch (err) {
	console.error(err)
}

 

  • Any 타입 : 어떤 것도 할당할 수 있는 타입.
let anything: any = 'dfd'
const ace: string = anything
const blue: number = anything
const customer: boolean = anything

any 는 어디든 할당 가능 하다.. 그러나. 타입스크립트가 나오게된, 계기를 생각하면, any는 최대한 사용하지 않는게 좋다. any 를 자유롭게 사용할거면, 뭐하러 타입스크립트를 사용하나??

 

  • Unknown 타입 : 어떤 것도 할당할 수 있지만, 정확히 무엇인지 알수 없는 타입
let unknown: any = 'dfd'
unknown = 123
unknown = { a: 'A' }
unknown = [1, 2, 3]
// Unknown 타입은, 다른 변수에는 할당할수 없음
// 타입 가드에 활용하면 좋음.
if (typeof unknown === 'string') {
  // unknown 이 string 값일 때만, 아래 코드 실행
  const achivement: string = unknown
}

그래서 어떤데이터가 올지 모르는 경우에는, any 타입보다는 unknown 타입을 많이 사용함.

 

Any vs Unknown

any 를 사용한 방식

let any: any = 'hello'
console.log(any.toUpperCase()) // 정상작동
any = 123
console.log(any.toUpperCase()) // 런타입 에러 발생

let unk: unknown = 'hello'
console.log(unk.toUpperCase()) // 정적 Error
unk = 123
console.log(unk.toUpperCase()) // 정적 Error

 

unknown 을 이용한 엄격한 타입 처리

let unk: unknown = 'hello'
if (typeof unk === 'string') { // 타입 string 경우에만
  console.log(unk.toUpperCase())
}
unk = 123
if (typeof unk === 'number') { // 타입 number 경우에만
  console.log(unk.toUpperCase()) // 정적 Error
}

 

 

  • union 타입 : 2개이상의 타입이 허용되는 경우
let uni: string | number
let uni2: string | number | number[]

 

  • intersection 인터섹션 타입 : 2개 이상의 타입이 병합되어 1개의 타입이 되는 경우
type UserA = {
  name: string
  age: number
}
type UserB = {
  isValid: boolean
}

// 타입 합성
const userC: UserA & UserB =  {
  name: 'C', age: 31, isValid: true
}

 

 


타입 추론

타입 추론(Inference) : 어떠한 코드의 내용을 근거로 판단(추론)을 이끌어냄.

코드 상에 최소 1개 이상의 힌트는 있어야 한다.

(컴퓨터도 힌트가 있어야 추론이 가능하기에...)

lat age = 45 // number
const name = '강감찬' // string

코드를 보면서 스스로 생각하는 타입 추론도 연습해야 한다.

타입 추론은 굳이 설명이 없어도 될듯

 


타입 단언과 할당 단언

타입 단언(Assertion) : '단언'이란, 주저하지 않고 딱! 잘라 말하는 것.

=> 개발자가 TS 에게, "이 코드는 이거다!!" 라고 단언하는 것. 물론 인간이 단언하는 컴퓨터에게 단언 하는것 이기에, 사용하는데 신중을 기해야 함.

  • as, ! (Not-Null 단언 연산자)
// as, ! (Not-Null 단언 연산자)

// case 1  
// as 를 이용하여 HTMLButtonElement 라고 단언
const btn = document.querySelector('button') as HTMLButtonElement
btn.classList.add('btn')
btn.id

const btn2 = document.querySelector('button')! // null 아니다 라는것만 단언
btn2.classList.add('btn')
btn2.id

// case 2
function toTwoDecimals(val: number | string, isNum: boolean) {
  if (isNum) {
    ;(val as number).toFixed(2) // isNum = true 경우, val 을 number 라고 단언
  } else {
    ;(val as string).slice(0, 2)// isNum = false 경우, val 을 string 라고 단언
  }
}

// case 3
const json = '{ "name": "Hero", "age":85 }'
// as 를 통해 명확한 객체 내용 단언 => (객체 내용은 ~ 로 구성되어 있다)
const user = JSON.parse(json) as {name: string, age: number}

// case 4
let num!: number // ! 붙힘으로써, null 도 undefind 도 아니라는 것을 명시
console.log(num)

 

 


타입 가드

타입 추론이 가능한 특정 범위(scope) 안에서 타입을 보장

  • typeof
  • instanseof
  • in

단언은 런타임시 에러발생을 시킴 (null 될수도 있음)

→ 타입가드를 통해 훨씬더 안전하게 코딩하자

// case 1
const btn = document.querySelector('button')

if (btn) { // btn 이 null 아닌 경우
  btn.classList.add('btn')
  btn.id
}
// 좀더 명시적으로
if (btn instanceof HTMLButtonElement) { // btn 이 HTMLButtonElement 인 경우만
  btn.classList.add('btn')
  btn.id
}
// case 2
function toTwoDecimals(val: number | string) {
  if (typeof val === 'number') { // typeof 를 사용
    val.toFixed(2) 
  } else {
    val.slice(0, 2)
  }
}

 

아래는 조금 복잡하지만 유용한 형태

// case 3
type UserA = {
  name: string
  age: number
}
type UserB = {
  id: string
  email: string
}

// 타입가드가 목적인 함수 isUserA
function isUserA(user: unknown): user is UserA { // user 이 UserA 인가? 라는 의미를 반환(boolean)
  if(user && user.constructor === Object) {
    const u = user as UserA
    return typeof u.name === 'string' && typeof u.age === 'number' // UserA 이면 true 반환
  }
  return false
}

fetch('<https://exam.site>')
  .then(res => res.json())
  .then((user: UserA | UserB) => {
    if(isUserA(user)) { // UserA 이면 true
      console.log(user.name[0])
      console.log(user.age - 10)
    }
  })

 

 


타입 별칭(Alias)

새로운 타입 조합 생성

type MyTypeName = string | number // MyTypeName 은 string , number 일수도 있다는 의미
type MyArray = MyTypeName[] // string , number 로 이뤄진 배열
type UserA = {
  name: string
  age: number
}
type UserB = {
  isValid: boolean
}
type UserX = UserA & UserB // 2개를 병합한 UserX

// 위의 type을 이용한 예시
const a: MyTypeName = 'A'
const b: MyArray = [1, 'a', 'b', 5, 7]
const userA: UserA = {
  name: 'koko',
  age: 78,
}
const userB: UserB = {
  isValid: false,
}
const userX: UserX = {
  name: 'jaja',
  age: 99,
  isValid: true,
}

내가 자주사용하는 나만의 Type 생성

타입스크립트 인데, 의외로 이부분에서는 자유도를 부여한다.(생산성을 높이려는 의도)