TypeScript - 타입스크립트와 타입 종류

uk·2023년 3월 22일
1

TypeScript

목록 보기
1/5

타입스크립트란?

TypeScript는 마이크로소프트에서 개발 및 관리되고 있는 JavaScript의 Superset(TypeScript ⊃ JavaScript) 언어이며 JavaScript의 모든 기능을 지원하고 JavaScript의 단점을 보완하기 위해 사용한다.

JavaScript는 동적 타입 언어로 진입 장벽이 낮고 타입이 정해져있지 않아 생산성이 높다는 장점이 있지만 런타임 시점에 자동으로 타입이 변환되기 때문에 의도하지 않은 결과를 나타낼 수 있다.

TypeScript는 정적 타입 언어(정적 타입 검사 제공)로 타입을 명시해 컴파일 환경에서 에러를 발생시키기 때문에 버그를 미리 방지할 수 있고 예측 가능하며 가독성이 좋고 디버깅이 쉽기 때문에 유지보수가 좋다.

하지만 브라우저는 TypeScript를 이해하지 못한다. .ts 확장자로 파일을 작성하면 타입스크립트 컴파일러를 통해 .js 파일로 컴파일 되고 런타임 환경에서 사용할 수 있게 된다.

즉, TypeScript는 프로그래밍 언어이자 컴파일러이다.


타입스크립트 설치 및 컴파일

// 타입스크립트 설치(global)
npm i -g typescript

// 타입스크립트 컴파일
tsc app.ts

// 컴파일된 app.js 파일이 생성된다.
app.js

// 컴파일된 app.js 파일 실행
node app.js

타입스크립트를 설치하고 .ts 확장자를 통해 새로운 파일을 만든다.

런타임 환경에서 타입스크립트를 실행시킬 수 없기 때문에 tsc 명령어를 통해 타입스크립트 파일을 자바스크립트 파일로 컴파일하는 과정이 필요하다.

이후 컴파일된 .js 파일을 node 명령어를 통해 실행시킨다.

twc -w

File change detected. Starting incremental compilation...
Found 0 errors. Watching for file changes.

터미널에서 tsc -w 명령어 실행을 유지시키면 모든 .ts 파일을 자동으로 감시 및 분석하여 컴파일이 진행된다.


ts-node

// ts-node 설치(global)
npm i -g ts-node

// app.ts 파일 실행
ts-node app.ts

타입스크립트를 런타임 환경에서 실행시키기 위해서는 위와 같은 컴파일 과정이 필요하다.
이때 ts-node를 사용하면 .ts 파일을 컴파일 과정 없이 ts-node 명령어를 통해 바로 실행할 수 있다.

주의점 - 실제 .js 파일로 컴파일되진 않고 .ts파일을 실행시키기만 한다.


타입스크립트의 타입

1. Number Type

let num1: number = 10;
let num2: number = 0.05;

Javascript와 마찬가지로 정수와 실수 구분 없이 number 타입 으로 표기한다.


2. String Type

let str1: string = 'Typescript!';  
let str2: string = `Typescript!!`;

Javasciprt와 마찬가지로 큰 따옴표("")나 작은 따옴표('')를 사용하여 문자열 데이터를 표현하고 string 타입으로 표기한다.

템플릿 리터럴 사용이 가능하다.


3. Boolean Type

let isAdult: boolean = true;

true, false 값을 가지는 boolean 타입으로 표기한다.


4. undefined & null

let isNull: null = null;
let isUndefine: undefined = undefined;

undefined, null도 데이터 타입 이자 하나의 값이기 때문에 TypeScript에서도 하나의 타입으로 처리되며 null, undefined로 표기한다.


5. Array Type

let num: number[] = [1, 2, 3, 4, 5];
let cats: Array<string> = ['nava', 'coco', 'mozzi'];

두가지 방법으로 배열 타입을 선언할 수 있다.
1. 배열 요소들의 타입 뒤에 [] 표기
2. Array 작성 후 <> 안에 배열 요소들의 타입 표기

<> 안에는 하나의 타입만 들어갈 수 있다.


6. Tuple Type

let user: [number, string, boolean] = [20, 'test', true];

console.log(user[1].toUpperCase());  // TEST
console.log(user[0].toUpperCase());  // Property 'toUpperCase' does not exist on type 'number'.

튜플은 길이와 타입이 고정된 배열이다. 여러 타입을 가질 수 있고 배열의 index마다 타입이 정해져 있기 때문에 정확한 요소의 index에 접근해야한다.

요소 뒤에 .을 찍어보면 사용할 수 있는 함수 목록이 나온다.


7. Object Type

let obj: object = { ... };

object 타입은 모든 객체를 수용하는 타입으로 객체의 프로퍼티 타입들이 any로 지정되기 때문에 안정성을 보장하지 않으므로 편리하지만 지양하는 것이 좋다.

let user: {name: string, age: number} = {
	name: "navi",
	age: 5
    adult: false;  // 타입 에러
}

위와 같이 객체의 프로퍼티 타입들을 각각 명시해 주는 방법을 사용하는 것이 좋다.

객체 리터럴은 명시한 프로퍼티만 지정할 수 있으므로 name, age 이외의 프로퍼티를 작성하면 에러가 발생한다.


8. Any Type

let test: any = 10;
test = 'text';
test = false;

타입 체크를 하지 않으며 모든 타입을 허용한다. any 타입을 사용하면 타입에 관계없이 재할당이 가능하며 자동완성 기능이 제공되지 않는다.

any 타입은 JavaScript를 사용하는 것과 별 차이가 없기 때문에 union 타입을 사용하는 것이 좋다.

let test: any = [1, 2, 3];

console.log(test[2].toLowerCase());  // undefined

엄격한 타입 체크를 하지 않기 때문에 변수에 할당된 값이 가지지 않는 메서드나 속성에 접근해도 에러가 나지 않지만 undefined를 반환한다.


9. Union(합집합) Type

let test: number | string;
test = 1234;
test = 'test';

const getValue = (val: number | string): void => {
  if (typeof value === "number") {
    console.log(`number: ${val}`);
  } else {
    console.log(`string: ${val}`);
  }
}

유니온 타입은 파이프 연산자( | )를 사용해서 여러 타입을 동시 지정한다. 함수에 여러 타입이 들어와 타입에 따라 값을 처리할 경우 유용하다.

주의점

interface Cat {
  name: string;
  age: number;
}

interface Dog {
  name: string;
  gender: string;
}

const test = (a: Cat | Dog) => {
  console.log(a.name);
  console.log(a.age);  // 오류
  console.log(a.gender);  // 오류
}

유니온 타입인 값은 공통된 프로퍼티에만 접근할 수 있다. 공통되지 않는 프로퍼티에 접근하려면 타입 가드를 사용해야한다.


장점

1. 자동완성 기능
타입 추론을 하기 때문에 타입에 관련된 함수 자동완성 기능을 제공한다. 변수 뒤에 .을 찍으면 타입에 따라 사용할 수 있는 함수 목록이 나타난다.

2. 가독성 향상

let test: number | string | boolean;

test 변수는 숫자, 문자열, 불리언 타입 중 하나의 값을 가질 수 있다는 것을 명시적으로 작성하여 코드를 이해하기 쉽게 만들어준다.


9-1. 타입 가드

interface Cat {
  name: string;
  age: number;
}

interface Dog {
  name: string;
  gender: string;
}

const test = (a: Cat | Dog) => {
  if ('age' in a) {
    console.log(a.name);  // navi
  }
  
  if ('gender' in a) {
    console.log(a.name);  // mozzi
  }   
}

test({name: 'navi', age: 3});
test({name: 'mozzi', gender: 'female'});

타입이 여러개(union type)일 경우 공통 프로퍼티에만 접근할 수 있다. 타입 가드는 TypeScript 컴파일러가 타입 추론을 할 수 있도록 돕는 방법으로 특정 코드 블록(if)에서 typeof, in, instanceof, === 연산자 등을 통해 타입의 범위를 제한하고 타입 안정성을 보장해준다.

in 연산자 - 해당 프로퍼티가 객체에 존재하는지 여부를 검사하는 연산자


10. Intersection(교집합) Type

interface Cat {
  name: string;
  age: number;
}

interface Dog {
  name: string;
  gender: string;
}

type Animal = Cat & Dog;

// Animal의 타입
{
   name: string;
   age: number;
   gender: string;
}

여러 타입을 & 연산자를 사용해 하나의 타입으로 결합하는 방식으로 여러 타입을 모두 만족하는 하나의 타입이다.

유니온 타입 - or, 인터섹션 타입 - and


const test = (a: Animal) => {
  console.log(a.name);  // navi
  console.log(a.age);  // 3
  console.log(a.gender);  // male
}

test({name: 'navi', age: 3, gender: 'male'});

Animal 타입은 Cat, Dog 인터페이스에 정의된 모든 프로퍼티를 포함하기 때문에 타입 가드가 필요하지 않고 모든 프로퍼티에 접근할 수 있다.

그러므로 인터섹션 타입은 전달인자를 전달할 때 반드시 모든 프로퍼티를 전달해야 한다.
-> 유니온 타입은 타입 가드를 사용해야 하고 전달인자를 전달할 때 하나의 인터페이스가 가지는 프로퍼티, 모든 프로퍼티 등 선택하여 전달할 수 있다.


Union, Intersection, 합집합.. 교집합..?

유니온 타입은 공통 프로퍼티에만 접근할 수 있다. 그런데 합집합..? 마찬가지로 인터섹션 타입은 여러 타입을 결합시켜 모든 프로퍼티를 포함시키는데 교집합인 것이 이해가 안됐다.

집합의 관점에서 타입스크립트 바라보기


타입 추론(Type Inference)

타입 추론은 타입을 표기하지 않아도 TypeScript가 자동으로 코드를 분석하여 타입을 유추하는 기능이다.

1. 기본 타입 추론

let test = 1234;
test = 'str';  // Type 'string' is not assignable to type 'number'.

test 변수의 타입이 생략되었지만 TypeScript는 타입을 자동으로 number로 추론하고 다른 타입이 할당될 수 없다.


2. 최적 공통 타입 (Best common type)

let test2 = [0, 1, null];

여러 표현식에서 타입 추론이 발생할 때 해당 표현식의 타입을 사용하여 "최적 공통 타입"을 계산한다. test2 배열의 타입 후보는 number, null 이다.

최적 공통 타입 알고리즘은 각 후보의 타입을 고려하여 모든 후보의 타입을 포함할 수 있는 타입을 선택한다.

let test2: (number | null)[]

test2의 타입이 union 타입으로 추론되었다.


3. 문맥상의 타이핑(Contextual Typing)

const add = (x: number, y: number) => {
  return x + y;
}

함수의 리턴 타입을 명시하지 않아도 TypeScript는 문맥상 리턴값의 타입이 number임을 자동으로 추론한다. number 타입간 연산은 리턴값이 number인 것을 컴파일러가 스스로 판단한다.

하지만 타입을 명시함으로써 버그를 방지하고자 사용하는 것이 TypeScript인데 JavaScript와 같이 타입을 생략해버리면 TypeScript를 왜 사용하는지 의문이 생길 수 있다.

const num = 10;

const num: number = 10;

위 코드에서 num은 const 키워드로 선언되었기 때문에 재선언, 재할당이 불가능하다.
이러한 상황에서 굳이 타입을 명시하는 것은 비효율적이기 때문에 TypeScript가 판단하도록 하는 것이다.


타입 추론의 장단점

장점 - 명시적으로 타입을 지정하지 않아도 TypeScript가 변수의 타입을 유추하기 때문에 가독성이 향상되고 코드 작성 시간이 단축되어 생산성이 향상된다.

단점 - 타입 추론이 잘못될 경우 오류가 발생할 수 있고 명시적인 타입 지정이 꼭 필요한 경우가 있기 때문에 상황에 따라 정확하게 타입 추론을 사용해야 한다.

profile
주니어 프론트엔드 개발자 uk입니다.

0개의 댓글