/ TS

타입스크립트 배워보기!

목차


타입스크립트 개요

일반 자바스크립트 컴파일되는 자바스크립트 상위 호환(Superset)이다. 즉 자바스크립트의 모든 구문과 기능을 지원 하며 자바스크립트에 포함 되지 않은 더 풍부한 구문과 추가 기능을 제공한다.

JS에서 변수는 문자열, 숫자, 불린 등 여러 타입의 값을 가질 수 있으므로 약한 타입 언어라고 표현하여 비교적 유연하게 개발할 수 있는 환경을 제공하지만 런타입 환경에서 쉽게 에러가 발생하는 단점이 있다.

반대로 타입스크립트는 강한 타입 시스템을 적용하여 에러를 컴파일 환경에서 코드를 입력하는 동안 체크할 수 있다.

그리고 타입스크립트는 프로그래밍 언어인 동시에 컴파일러이다. 타입스크립트를 자바스크립트로 컴파일한다.

버그를 최소화 -> 쉬운 유지 보수 -> 질 좋은 코드

img_1

타입스크립트 설치

공식홈페이지로 이동하여 install locally 버튼을 클릭하면 npm으로 설치할 수 잇는 명령어가 나온다

npm install typescript

타입스크립트 다루기

간단한 예제들을 통해 배워보겠다.

아래와 같이 매개변수에 타입을 지정하여 함수를 실행할 것이다. 타입스크립트는 컴파일러이기 때문에

tsc 파일명을 통해 자바스크립트로 변환 하자.

그럼 같은 이름에 js파일이 생성되면 node명령어를 통해 실행할 수 있다.

tsc -w 파일명 을 사용하면 따로 컴파일 명령어를 사용하지 않아도 실시간으로 컴파일을 해준다

app.ts

// 매개변수에 타입을 지정
function addName(name: string) {
    console.log(name)
}
// 해당 인자로 1 숫자를 넣으면 에러가 발생한다
addName("철수")
// app.ts 컴파일
tsc app.ts

// 위 컴파일이 끝나면 app.js 생성
// "김"출력
node app.js

------------------------------
철수

중복된 함수 구현에러 발생시 (vscode 버그라고함)

tsc --init으로 해결 가능

1. Static Typing

타입스크립트의 큰 특징 중 하나는 Static Typing 기능을 자바스크립트 언어에 지원하는 것이다

Static Typing?

타입을 선언하고 선언된 타입의 맞는 값만 할당이나 반환 되는 것

Type Inference (타입 추론)

타입스크립트에서는 타입 표기가 없는 경우 코드를 읽고 분석하여 타입을 유추 할 수 있는 특징이 있다

app.ts

// 아래와 같은 코드를 입력시 두번째 a에서 할당값이 숫자가 아니기 때문에 에러가 발생함
let a = 5;
a = 'k'

// 타입스크립트는 자바스크립트의 superset이기 때문에 정상적으로 컴파일 된다.
let a = 5;
a = 4

// 이미 객체 property의 값의 타입 추론이 됨
// 할당 값을 바탕으로 타입 추론
let person = {
    name: "철수",
    age: 31
    address: "서울"
    action: function() {
        console.log(`${this.name}${this.address}에 산다`)
    }
}

// 위 주석 때문에 name에 숫자로 재할당 하려하면 
// 에러 발생
person.name = 123

// function에 마우스를 오버하면 -연산자를 통해 함수의 리턴 타입이 숫자일 것을 추론
function add (number) {
    return 1 - number;
}

Type 명시

변수를 선언할 때, 변수 값의 타입을 명시함으로써 변수 값의 데이터 타입을 지정

let n: number = 1
let name: string = "철수"
let age: number = 23
let gender: string = ""
let address: string = "서울"
let adult: boolean = true

// 다음과 같이 `object`로 함수에서 반환되는 값의 타입을 지정해 줄 수 있다.
// 아래와 같이 반환되는 타입을 any, void가 아닌 값을 지정시 return값이 반드시 와야함
function getPerson(age: number):object {
    return null
}

// 아래처럼 명시된 구조로 이용할 수 있다.
// 반환되는 객체의 구조를 타입으로 지정
// 아래와 같이 반환 타입을 명시할 수 있지만 지저분해 보이는 것을
// interface를 이용하여 효과적으로 처리할 수 있다.
function getPerson(age: number):{
    n: number
    name: string
 	age: number
 	gender: string
	address: string
	adult: boolean
} {
    return null
}

Interface로 타입의 구조를 정의하는 방법

타입스크립트에서는 interface네이밍시 i를 붙이지 않고 대문자로 이용

interface PersonCard {
    n: number
    name: string
 	age: number
 	gender: string
	address: string
	adult: boolean
}

// person인터페이스를 이용하여 반환값이 person구조를 가지도록 강제됨
function getPerson (n: number): PersonCard {
    return {
        n: 1,
        name: "철수",
 		age: 31,
 		gender: "",
		address: "서울",
		adult: true
    }
}

// 아래와 같이 반환값이 다르다면 에러가 발생
function getPerson(n: number): PersonCard {
    return "Hello world"
}

위 와 같이 인터페이스를 타입으로 가지는 값은 인터페이스의 구조를 그 값으로 가지도록 강제된다

? 선택적 프로퍼티 활용

강제되는interface를 좀 더 유연하게 이용하기 위해서는 interface내 에 정의된 특정 프로퍼티(선택적 프로퍼티)가 있어도 되고 없어도 된다면 Option의 기호인 ?를 붙여서 사용한다

let person = {
    	n: 1,
        name: "철수",
 		age: 31,
 		gender: "",
		address: "서울",
		adult: true
}
// 특정 프로퍼티 뒤에 ?를 붙이면 유연하게 사용 가능
interface PersonCard {
    n: number
    name: string
 	age?: number
 	gender: string
	address: string
	adult: boolean
}

// age가 있어도되고 없어도 된다
function getPerson (n: number): PersonCard {
    return {
        n: 1,
        name: "철수",
 		gender: "",
		address: "서울",
		adult: true
    }
}

// 아래와 같이 interface의 코드를 재사용이 가능하다
function savePerson(person: PersonCard): void {
    
}

savePerson(person)

인터페이스에 프로퍼티로 메소드도 정의할 수 있다

메소드는 객체내에서 정의된 함수이므로 아래처럼 표현 가능

interface PersonCard {
    n: number
    name: string
 	age: number
 	gender: string
	address: string
	adult: boolean
    // 아래와 같이 메소드를 정의 가능
    // addComment(comment: string): string
    addComment?:(comment: string) => string
}

readonly로 읽기 전용 정의

readonly 속성을 추가하여 읽기 전용 프로퍼티로 정의 가능하다

let person = {
    	n: 1,
        name: "철수",
 		age: 31,
 		gender: "",
		address: "서울",
		adult: true
}

interface PersonCard {
    readonly n: number
    name: string
 	age: number
 	gender: string
	address: string
	adult: boolean
    addComment?:(comment: string) => string
}


function savePerson(person:Person): void {
    Person.n = 2;  // readonly 속성 때문에 변경되지 않고 에러가 발생한다
}

savePerson(person)

interface는 코드가 렌더링 될때 아무런 영향력이 없다 타입스크립트 컴파일러가 자바스크립트로 컴파일할때 인터페이스를 코드에서 삭제함

interface는 타입스크립트에게 더 많은 정보를 제공하기 위해 존재

열거형Enum과 리터럴 타입

Enum 타입

Enum이란 연관된 아이템들을 함께 묶어서 표현 할 수 있는 수단

// enum 선언
enum GenderType {
    male,
    female
}


interface PersonCard {
    readonly n: number
    name: string
 	age: number
 	gender: GenderType // 기존 gender: string을 수정 
	address: string
	adult: boolean
    addComment?:(comment: string) => string
}

let person = {
    	n: 1,
        name: "철수",
 		age: 31,
 		gender: GenderType.male,  //gender: "남"을 수정
		address: "서울",
		adult: true
}

function savePerson(person: PersonCard): void {
    
}

savePerson(person)

enum코드를 자바스크립트 컴파일 된 자바스크립트 코드에서 확인해보면 아래와 같다

img1

enum 속의 아이템 male의 값은 0 , female의 값은 1을 나타낸다. 아이템의 개수가 늘수록 숫자가 늘어나는 방식이다. 이를 숫자 열거형(Numeric Enum)이라고 한다.

이와 반대로 문자형 열거형(String Enum)도 존재한다.

// 아래와 같이 enum의 string 값을 할당해주면 된다
enum GenderType {
    male = "male",
    female = "female"
}


interface PersonCard {
    readonly n: number
    name: string
 	age: number
 	gender: GenderType
	address: string
	adult: boolean
    addComment?:(comment: string) => string
}

let person: PersonCard = {
    	n: 1,
        name: "철수",
 		age: 31,
 		gender: GenderType.male,
		address: "서울",
		adult: true
}

function savePerson(n: number): void {
    
}

savePerson(person)

String Enum은 숫자 열거형 처럼 자동 증가하는 기능은 없지만 코드를 실행할 때 읽기 쉬운 값을 주는 장점이 있다.

enuminterface와 달리 자바스크립트 코드로 컴파일시 코드가 사라지지 않고 자바스크립트코드에 나타난다.

이것은 enum이 런타임에 존재하는 실체 객체라는걸 나타냄

literal 타입
interface PersonCard {
    readonly n: number,
    name: string,
 	age: number,
 	gender: 'male' | 'female', // |을 이용하여 값을 구분 시켜주면 된다
	address: string,
	adult: boolean,
    addComment?:(comment: string) => string
}

let person: PersonCard = {
    	n: 1,
        name: "철수",
 		age: 31,
 		gender: 'male',    // 리터럴 타입의 맞춰 수정
		address: "서울",
		adult: true
}

function savePerson(person: PersonCard): void {
    
}

savePerson(person)

2. 데이터 타입

any 타입은 어떤 타입이든 가능하게 해준다.

let a: any = 5
a = "a"
a = true

하지만 타입스크립트에서 일반적인 규칙은 타입에 관한 더많은 정보를 명시할 수록 좋다

따라서 any타입 보다는 좀 더 명시적으로 타입을 정해서 효과적으로 코드를 유지 보수 하는 것이 좋다

유니온 타입

제한된 타입을 동시에 지정해서 사용하고 싶을때 사용한다

let price: number|string = 1000 // `|`를 이용하여 선택적 타입 지정 가능
price = "free"

파이프 라인(|)을 사용하여 간단하게 타입을 지정할 수 있다.

Type Aliases

만약 위와 같은 조합이 여러개 반복될 경우 같은 코드를 여러번 작성해야할 경우도 있을 것이다

이때 같은 코드를 여러번 반복하는 것 보다는 코드를 타입으로 지정하여 재활용 하는 것이 좋다

이때 Type Aliases를 사용한다.

type price = number | string; // type 키워드를 사용하여 새로운 타입 선언하는 방법

Type Guard

유니온 타입을 사용할 때 코드 검증을 수행하는 것을 Type Guard라고 함

type NumOrStr = number | string;
let item: number;

const setItemPrice = (price: NumOrStr): void => {
    item = price; // item에서 에러가 발생 
				  // 매개변수 타입이 number와 string이 올 수 있지만 반면에
    			  // item에는 number 밖에 올수 없기 때문에 코드를 고쳐야함
}

setItemPrice(50)

위 코드를 아래와 같이 typeof 연산자와 조건문을 사용하여 변환해 주면 에러를 해결 할 수있다

이 행위를 Type Guard라고 한다

type NumOrStr = number | string;
let item: number;

const setItemPrice = (price: NumOrStr): void => {
    if (typeof price !== "string") item = price; 
}

setItemPrice(50)

3. 선택적 매개 변수와 기본 매개변수

타입스크립트에서는 함수에 정의된 모든 매개 변수가 함수에 필요하다고 가정하기 때문에 선언된 함수의 매개변수와 호출된 함수의 인자를 비교하여 수가 일치하지 않으면 에러를 발생한다

// 컴파일 가능한 함수
function chat(who:string, message:string): void {
    console.log(`${who}: ${message}`)
}
chat("안녕", "철수")

// 에러 발생
function chat(who:string, message:string): void {
    console.log(`${who}: ${message}`)
}
chat("안녕")

// 에러 해결, 선택적 매개변수
function chat(who:string, message?:string): void {
    console.log(`${who}: ${message}`)
}
chat("안녕")

중요

전달되는 매개변수가 여러개이고 이중 몇가지만 선택적 매개변수인 경우 반드시 선택적 매개변수들은 필수 매개변수 뒤에 위치해야한다

위의 마지막 함수처럼 선택적 매개변수를 사용할때 그 값을 찾지 못할 경우 그 값이undefined가 되는데 매개변수에 default 값을 지정하여 해결할 수있다.

// 선택적 매개변수, 기본 매개변수
// 타입스크립트의 타입 추론 특징을 이용하여 string으로 타입을 명시하지 않아도 된다
function chat(who:string, message? = "guest"): void {
    console.log(`${who}: ${message}`)
}
chat("안녕")

// 화살표 함수로 변경
const chat = (who:string, message? = "guest"): void => console.log(`${who}: ${message}`)
chat("안녕")