24_타입스크립트 문법
FrontEnd/JavaScript

24_타입스크립트 문법

728x90

자바스크립트 같은 경우, 아래와 같이 자료형이 정해져있지 않아 실수하기가 쉽다.

 

let value = 5;
value = '안녕하세요';
value = [1,2,3,4,5];
value = null

 

TypeScript를 사용하면 이같은 실수를 방지하기 편하고, ide(vs코드같은)에서 기능을 지원받기 쉽다. 예를 들어 내장함수를 쓸때 파라미터로 무엇이 들어간다던지 등의 기능을 볼 수 있다. 

 

그럼 타입스크립트의 문법을 한번 알아보자

 

먼저 파일하나를 만든 후에,

 

yarn init -y

package.json파일을 하나 만들어준다.

 

yarn add typescript ts-node  //ts-node : 콘솔창에서 typescript사용 가능
yarn run tsc --init //타입스크립트를 위한 설정

그후 위 라이브러리들을 설치해 준다.

 

 

src폴더를 하나만들고 그 아래 아래와 같은 간단한 코드를 작성해 보자.

//practice.ts
const message : string = '나는 정민규';
console.log(message)

 

그다음 아래코드를 실행하면 practice.js파일이 나타난것을 볼 수 있다.

 

yarn run tsc

 

 

이때 tsconfing.json파일안의 Outdir의 주석을 풀고 아래와 같이 고쳐주면 dist파일안에 js파일이 저장되게 된다.

 

practice.js파일을 지우고 다시 run tsc를 하면

yarn run tsc

 

dist 파일 안에 practice.js파일이 나타난것을 볼 수있다.

 

"use strict";
const message = '나는 정민규';
console.log(message);

일단은 string으로 지정해줬던 부분이 사라져있음이 보인다.

 

터미널을 통해 실행해보아도 잘 실행됨을 볼 수 있다

 

또한 ts-code를 같이 설치했기에 굳이 js파일로 안바꾸어도 실행할 수 있다.

 

yarn run ts-node ./src/practice.ts

위처럼 실행시키면 된다.

ts-node로 실행

 

 

타입스크리트에 자료형을 하나만 고를 수 있는 것은 아니다. or연산자를 통해 여러개중 하나만 고르게 하는것도 가능하고, 특정 문자를 지정해서 그 문자들만 사용하게 하는것도 가능하다.

//practice.ts
let count = 0; // 숫자
count += 1;
//count = '나는 정민규'; // 에러 발생

const message: string = 'hello world'; 

const done: boolean = true; 

const numbers: number[] = [1, 2, 3]; 
const messages: string[] = ['hello', 'world']; 

//messages.push(1); // 에러 발생

let mightBeUndefined: string | undefined = undefined; // string or undefined 
let nullableNumber: number | null = null; // number or null 

let color: 'red' | 'orange' | 'yellow' = 'red'; // red, orange, yellow 중 하나만
color = 'yellow';
//color = 'green'; // 에러 발생

 

함수를 만들때도 반환하는 값의 타입을 지정해줄 수 있다.

 

//practice.ts

function sum (x: number ,y : number) : number /*반환값을 지정*/ {
    return x + y;
    //return 'hi' //에러
}

const result = sum(1,2);

몇가지 예를  더 보자.

 

//practice.ts

function sumArray(numbers : number[] ) : number {
    return numbers.reduce((acc,current) => acc + current, 0 )
}

const total = sumArray([1,2,3,4,5])
console.log(total)

 

반환값이 없으면 void를 사용하면 된다.

//practice.ts

function returnNothing() : void {
    console.log('아무말대잔치')
}

returnNothing();

 

함수역시 반환형 타입을 여러개 지정할 수도 있다.

//practice.ts

function returnStringOrNumber() : string | number  {
    return 4;
}

returnStringOrNumber();

 

 

interface

class 혹은 객체를 지정될때 사용된다

 

 

//practice.ts

interface Shape{
    getArea() : number;
}

class Circle implements Shape {   //Circle클래스가 Shape를 충족해야함
    radius : number;

    constructor(radius : number){
        this.radius = radius;
    }

    getArea() {
        return this.radius * this.radius * Math.PI;
    }
}

class Rectangle implements Shape {
    width : number;
    height : number;
    constructor(width : number, height :  number) {
        this.width = width;
        this.height = height;
    }

    getArea() {
        return this.width * this.height
    }
}

const circle : Circle = new Circle(5);    //:Circle 생략해도 괜찮다.
const rectangle = new Rectangle(2,5);     //:Rectangle생략해도 괜찮다.

 

자바스크립트의 객체선언에서  타입을 지정해주는것말고는 똑같다.

 

 

특히, Shape형태를 모아둔 배열을 아래처럼 선언하여 구했던 값들을 출력하게도 할 수 있다.

const shapes : Shape[] = [circle,rectangle];

shapes.forEach (shape => {
    console.log(shape.getArea());
})

 

 

객체 constructor에서 public과 private로 만들어주면 굳이 값을 일일히 넣어주지 않아도 괜찮다

interface Shape{
    getArea() : number;
}

class Circle implements Shape {   //Circle클래스가 Shape를 충족해야함
    

    constructor(public radius : number){
        
    }

    getArea() {
        return this.radius * this.radius * Math.PI;
    }
}

class Rectangle implements Shape {
    
    constructor(private width : number, private height :  number) {
        
    }

    getArea() {
        return this.width * this.height
    }
}

const circle : Circle = new Circle(5);    //:Circle 생략해도 괜찮다.
const rectangle = new Rectangle(2,5);     //:Rectangle생략해도 괜찮다.

console.log(circle.radius) //5
//console.log(rectangle.width) //에러발생, private라서

const shapes : Shape[] = [circle,rectangle];

shapes.forEach (shape => {
    console.log(shape.getArea()); //78.5398, 10
})

 

상속도 당연히 사용할 수 있다.

 

//practice.ts

interface Person {
    name : string;
    age? : number; //age가 있을수도 있고, 없을수도 있음
}

interface Developer extends Person {    
    skills : string[];
}

const person : Person = {
    name : '나는정민규',
    age : 25,
    //skills : '파이썬'  //에러 발생
}

const mingyu : Developer = {
    name : '정민규',
    age : 25,
    skills : ['파이썬', 'JS']
}

 

 

Type Alias를 사용해도 위 코드를 똑같이 변경할 수 있다.

 

//practice.ts

type Person = {    // =을 붙여줘야함!
    name : string;
    age? : number; //age가 있을수도 있고, 없을수도 있음
}

type Developer  = Person & {      //상속할때
    skills : string[];
}

const person : Person = {
    name : '나는정민규',
    age : 25,
    //skills : '파이썬'  //에러 발생
}

const mingyu : Developer = {
    name : '정민규',
    age : 25,
    skills : ['파이썬', 'JS']
}

//Type Alias쓸때 사용할수 있는부분
type People = Person[];   

const people : People = [person,mingyu]; 

type Color = 'red'|'orange'|'yellow';
const color : Color = 'red'

 

 

그렇다면 언제  interface를 사용하고 언제 Type Alias를 사용해야할까?

 

어떤 라이브러리를 위한 타입을 설정할땐 interface를 권장하고 있으며, 그외에는 어떤걸 사용해도 상관없지만 일관성을 유지하는것을 지향하고 있다.

 

 

 

Generics

TypeScript에서 여러 종류의 타입에 대해서 호환을 맞춰야 하는 상황에서 사용하는 문법

 

<T>

위와같은 형태를 가지고 있다.

 

사용예제를 한번 보자.

//practice.ts

function merge<T1,T2>(a : T1, b : T2 ) { //들어오는게 딱히 정해지지 않을때
    return {
        ...a,
        ...b,
    };
}

const merged = merge({ foo : 1 }, { bar : 2 }) 
merged.bar // merged안에 bar가 들어있다!

function merge2(a: object, b: object) {
    return{
        ...a,
        ...b
    }
}

const merged2 = merge2({foo : 1} , {bar : 2})
//console.log(merged2.bar) //에러발생

function merge3(a: any, b: any) {
    return{
        ...a,
        ...b
    }
}

const merged3 = merge3({foo : 1} , {bar : 2})
console.log(merged3.bar) //타입이 any

즉, any를 사용하면 타입이 any로 가져오게 되지만 Generics를 통해서 가져오면 type이 들어온 타입으로 보여지게 된다!

 

 

interface와 type alias에서도 generics사용이 가능하다.

 

//practice.ts

interface Items<T> {
    list : T[]
};

const items : Items<string> = {
    list : ['a','b','c']   //list가 string이어야 함
}
//practice.ts

type Items<T> = {
    list : T[]
};

const items : Items<string> = {
    list : ['a','b','c']   //list가 string이어야 함
}

 

 

여러개의 generics를 사용하는것도 가능하다.

 

type Items<T,V> = {
    list : T[]
    value : V
};

const items : Items<string,number> = {
    list : ['a','b','c'],   //list가 string이어야 함
    value : 5
}

 

 

지금까진 배운 내용을 가지고 간단하게 queue구조를 한번 만들어보자.

 

 

//practice.ts

class Queue<T> {
    list : T[] = []; //T로이루어진 배열

    get length() {
        return this.list.length;
    }


    enqueue(item : T) {
        this.list.push(item);
    }

    dequeue() {
        return this.list.shift();
    }
}

const queue = new Queue<number>();
queue.enqueue(0);
queue.enqueue(1);
queue.enqueue(2);

while (queue.length > 0) {
    console.log(queue.dequeue());
}

 

잘 실행되는 것을 볼 수 있다.

 

다음글에서는 이 타입스크립트로 리액트 컴포넌트를 만드는법을 다루겠다.

728x90