[MGS 3기 - 15일차] TypeScript 기초

박철연·2022년 5월 2일
0

MGS STFE 3기

목록 보기
15/35

온라인 강의 커리큘럼 상으로 자바스크립트가 드디어 끝났습니다. 자바스크립트는 앞으로 개인적인 복습과 프로젝트로 지속적으로 공부할 생각이고, 교육 과정에서 새롭게 시작한 타입스크립트를 정리글에 담아보았습니다.

해당 정리글은 교육 과정 중 학습한 내용과 제가 개인적으로 요약한 타입스크립트 이론을 바탕으로 작성하였습니다.

타입스크립트 소개

TypeScript란?

보통 타입스크립트를 검색해보면 "자바스크립트의 Superset"이라는 표현이 많이 나옵니다.

슈퍼셋이 무슨 말일까요? 슈퍼셋은 상위 확장, 내지는 상위 호환의 의미를 가지고 있습니다. 즉, 타입스크립트는 자바스크립트의 상위 호환격 개발 언어라고 볼 수도 있겠습니다.

바로 위쪽의 이미지처럼, 타입스크립트는 자바스크립트라는 언어를 포괄하는 슈퍼셋 언어이며, 따라서 자바스크립트와 대부분의 문법, 메커니즘 등을 공유합니다.

JavaScript의 한계점

그렇다면 타입스크립트가 어떤 특징을 가지고 있길래 자바스크립트의 상위 확장이라고 불리는 걸까요? 이를 이해하기 위해서는 먼저 자바스크립트의 특징을 알아볼 필요가 있습니다.

우리는 자바스크립트를 다루면서 수많은 데이터 타입들을 다루게 됩니다. 그런데 이 데이터 타입들의 홍수를 자바스크립트가 받아들이는 방법은 조금 느슨합니다.

자바스크립트는 타입과 관련된 분석을 런타임 이전에는 하지 않습니다. 즉, 일단 실행되고 나서야 데이터 타입에 대한 분석과 검사를 진행한다는 이야기입니다.

이러한 특징 때문에, 자바스크립트는 약간의 "융통성"이 내장되어 있습니다.

function numCheck(num) {
 return num == 0;
}

numCheck(0)  // true
numCheck(false)  // true
numCheck("")  // true
numCheck([])  // true

위의 코드 블럭에서, 숫자 타입 0 뿐만 아니라, false(Boolean), 빈 문자열, 빈 배열 모두 true를 리턴한다는 것을 알 수 있습니다.

각각 false, 빈 문자열, 빈 배열 모두가 0의 값을 갖는다고 간주되면서 강제로 형 변환을 하기 때문이죠.

이처럼 런타임에 가서야 데이터 타입에 대한 정의와 검사가 이루어지기 때문에, 자바스크립트를 동적 타입 언어라고 부릅니다.

TypeScript의 특징

우리가 대규모 프로젝트를 진행중이라서 문서 내에 코드가 수십만 줄이 있다고 생각을 해봅시다.

그런데 자바스크립트가 스스로 융통성을 발휘해 데이터 타입을 바꾼다면? 정확히 어디서 그러한 동적 타이핑이 일어났는지 찾을 수 있을까요?

또한 개발자의 의도가 함수에 숫자를 넣는 것이었다면? 함수에 숫자가 아닌 다른 데이터 타입을 넣었음에도 자바스크립트의 특징으로 인해 그 어떤 케이스에서도 에러가 뜨지 않았습니다.

타입스크립트는 바로 이러한 문제점들을 해결하기 위해 2012년, 마이크로소프트에 의해 오픈 소스로 개발된 자바스크립트 대체 언어입니다.

정리하자면, 타입스크립트는 실행 이전 단계부터 타입 에러에 대한 가능성을 감지하고, 다른 정적 타입 언어처럼 데이터 타이핑을 엄격하게 관리하는 자바스크립트의 잘 나가는 형제라고 볼 수 있겠습니다.

타입스크립트의 타입들

저번 글에서 간략하게 설명한 것처럼, 타입스크립트는 개발 단계에서 타입 지정을 엄격하게 시행합니다.

따라서 동적 타이핑 언어에 해당하는 자바스크립트의 데이터 타입을 더욱 안정적으로 쓸 수 있게 됩니다.

깊게 들어가면 당연히 알아야 할 것이 많겠지만, 기본적으로 타입스크립트는 자바스크립트의 문법에 타입과 관련된 부분을 엄격하게 다스리는 도구들이 추가된 형태라고 이해해도 무방할 것 같습니다.

이번 글에서는 실제 코드를 몇 가지 작성해서 타입스크립트에서 타입을 사용하는 방식을 알아보려고 합니다.

변수 타입 지정

원시 자료형

변수 타입 지정은 너무 간단합니다.

이미지에서 보시는 것처럼, 변수에 값을 할당하기 전에 데이터 타입을 입력해주면 끝입니다.

아래 쪽에 제가 nickname이라는 변수에 숫자를 할당하니까, 오류가 난 것 보이시죠?

타입 지정을 문자열로 한 변수라서 숫자 못넣는다고 타입스크립트가 친절히 알려줍니다.

타입스크립트에서는 보시는 것처럼 원시 데이터 타입에 해당하는 친구들을 변수에 할당할 수 있습니다.

따라서 null과 undefined도 타입 지정이 가능합니다.

객체와 배열

변수에 배열을 할당하는 것도 당연히 가능합니다.

코드에서 알 수 있듯이, 타입 지정 뒤에 배열의 대괄호를 붙여서 작성하시면 됩니다.

객체 타입은 조금 더 복잡한 데, 객체에 들어갈 key 값을 같이 작성해서 타입을 지정해줘야 합니다.

제가 num1과 num2 키 값에 숫자만 들어올 수 있다고 타입 지정한 상태기 때문에, 만약 obj 객체에 숫자가 아닌 데이터 타입을 넣게 되면 오류를 띄우겠죠?

Union 타입

가장 기초적인 타입 지정에 대해서는 알아봤는데, 여기서 한 가지 의문점이 들 수도 있습니다.

바로 변수에 두 개 이상의 타입을 넣을 때도 있을 수 있는데, 그럴 때는 어떤 식으로 코드를 작성해야 하는 걸까요?

그럴 때 우리는 |를 사용한 유니언 타입으로 타입 지정을 해주면 됩니다.

유니언 타입 활용법은 상기 이미지와 같습니다. 기본 타입 지정에 그냥 |를 사용해서 여러 가지 타입을 허용시켜 주는 것이라, 크게 어려운 부분은 없군요.

예시의 아랫부분에 나와 있듯이, 배열과 객체 타입의 변수에도 유니언 타입을 사용할 수 있습니다.

유니언 타입은 두 개 이상의 타입을 합친다기 보다는, 하나의 새로운 타입을 만들어 낸다고 이해하시는 것이 정확합니다.

any & unknown

여러 가지의 데이터 타입이 아니라, 변수에 어떤 타입이 들어올 지 확신할 수 없는 경우도 있지 않을까요?

타입스크립트는 그런 상황에도 다 준비가 되어 있습니다.

any

타입 지정을 any로 하면 어떤 타입의 데이터를 할당해도 다 받아줍니다.

다만, any는 어디까지나 일시적으로 타입 체크 기능을 해제하기 위해 사용하는 것이 좋습니다. any를 남발하면 타입스크립트의 존재 의의를 희석하는 것이나 마찬가지니까요.

unknown

뿐만 아니라, any와 비슷한 unknown도 있습니다.

unknow도 any와 똑같이, 변수에 지정해주면 모든 데이터 타입을 다 소화할 수 있습니다.

변수 profile을 선언한 부분부터가 중요한데요, 이 부분을 이해하면 any와 unknown의 차이를 쉽게 이해할 수 있습니다.

보시다시피 profile 변수에는 unknown을 지정해주었습니다. 그랬더니 타입 지정이 이미 완료된 변수가 참조할 수 없게 되죠.

다만, 타입 지정을 any로 했다면 이러한 경우에도 에러가 나지 않습니다. profile이 any였다면 var1부터 var3까지 모두 할당이 완료되었을 것입니다.

void

함수에 타입 지정을 할 때 알아두고 가야할 개념이 하나 있습니다. 바로 void입니다.

void는 간단합니다. 그냥 함수가 리턴할 값이 없는 경우에 void로 지정하면 됩니다.

제가 작성해본 double 함수의 타입 지정을 void로 해주었더니, 리턴 값을 작성하는 부분에 에러가 났군요.

void는 리턴 값을 허용하지 않기 때문일것입니다.

함수에 타입 지정해보기

타입스크립트는 함수에도 타입 지정을 할 수 있게 해줍니다. 일단 함수에 들어갈 인자에 타입 지정을 하고, 함수가 반환하는 값에도 타입 지정을 합니다.

기본적인 형태는 다음과 같습니다.

숫자를 넣으면 그 숫자를 두 배로 만드는 함수입니다. 들어가는 값(인자)도 숫자, 나오는 값(return 값)도 모두 숫자입니다.

당연히 다음과 같은 코드를 작성하면 에러가 뜰 겁니다.

숫자랑 문자열 'num'을 더하면 문자열이 됩니다. 따라서 함수의 리턴 값도 문자열이 되겠죠? 그러면 함수의 반환값이 숫자여야 한다는 우리의 타입 지정에 어긋납니다. 따라서 에러가 발생합니다.

타입 호환성

서브 타입

서브 타입(sub Type)이란 타입스크립트 내의 타입들 중 다른 타입의 하위로 편입될 수 있는 타입을 뜻합니다.

아래 코드 블럭을 참고하면 이해가 빠릅니다.

let sub1: 10 = 10;
let sup1: number = sub1;
sub1 = sup1; // error! Type 'number' is not assignable to type'1'.

sub1이라는 변수는 숫자 10만을 할당할 수 있는 타입입니다.

반면에, sup1은 number 타입의 값은 모두 넣을 수 있는 타입이죠.

그렇기 때문에 sub1은 sup1의 서브 타입이 될 수 있습니다. 실제로 코드 블럭에서도 sup1에 sub1을 할당해서 아무런 이상이 없는 것을 확인 할 수 있습니다.

하지만, 위치를 바꿔서 sub1에 sup1을 할당하려면 문제가 발생합니다.

이론상으로는 sup1의 값이 현재 10이기 때문에 문제가 없어보입니다. 하지만, sup1의 타입은 숫자 데이터를 모두 허용하는 반면, sub1은 오로지 10만 받는 타입이기 때문에 이러한 에러가 발생하는 것입니다.

sup1을 sub1에 할당하면, 추후에 sup1이 10이 아닌 숫자를 할당받았을 때 생기는 에러를 선제적으로 지적하는 것이라고 해석할 수도 있을 것입니다.

let sub2: number = 1;
let sup2: any = sub4;
sub2 = sup2;

한 가지 예외적인 상황이 있는데 바로 위 코드 블럭입니다.

sub2는 숫자만 받는 타입이고, sup2는 any 타입입니다. 당연히 sub2는 sup2의 서브 타입입니다.

그런데 sub2에 sup2를 할당해보면 에러가 발생하지 않습니다. 이렇게 any를 사용한 타입이 상위 타입으로 작용할 때에는 예외적으로 허용이 됩니다.

공변성

위에서 간략하게 살펴 본 것과 같이, 타입이 같거나 서브 타입일 경우 다른 변수에 할당이 가능해집니다. 이를 공변성이라고 합니다.

let sub3: string = '';
let sup3: string | number= sub3;

let sub4: { a: string; b: number } = { a: '', b: 1};
let sup4: { a: string | number; b: number } = sub4;

let sub5: Array<{ a: string; b: number }> = [{ a: '', b: 1 }];
let sup5: Array<{ a: string | number; b: number }> = sub5;

반병성

또한, 함수의 매개 변수 타입만 같거나 슈퍼 타입일 경우에도 할당이 가능합니다. 이를 반병성이라고 합니다.

class Person {}
class Developer extends Person {
coding() {}
}
class StartupDeveloper extends Developer {
burning() {}
}
function tellme(f: (d: Developer) => Developer) {}

// Developer => Developer 에다가 Developer => Developer 를 할당하는 경우
tellme(function dToD(d: Developer): Developer {
return new Developer();
});

// Developer => Developer 에다가 Person => Developer 를 할당하는 경우
tellme(function pToD(d: Person): Developer {
return new Developer();
});

// Developer => Developer 에다가 StartupDeveloper => Developer 를 할당하는 경우
tellme(function sToD(d: StartupDeveloper): Developer {
return new Developer();
});

// 사용자에게 선택옵션을 줌. (옵션을 켜지 않으며 융통성을 발휘해서 에러가 발생하지 않음)
// strictFunctionTypes 옵션을 켜면 에러를 통해 경고

타입 별칭 (Type Aliases)

타입 별칭을 쓰는 이유

타입 지정을 하나로 못 박기 힘들 때는 유니언 타입을 쓰면 됩니다. 그런데 변수에 지정해야 할 타입의 갯수가 상당히 많다면 어떨까요?

number도 되고, boolean도 되고, string, null 등등 다 가능하다고 생각해 봅시다.

그러면 타입 지정과 관련된 코드가 상당히 길어지고, 가독성을 떨어뜨리는 데 한 몫 하게 되지 않을까요?

자바스크립트에서 변수에 데이터를 담는 것 처럼, 타입 지정도 그릇에 담아줄 수 있습니다. 그 그릇이 바로 타입 별칭(Type Aliases)입니다.

타입 별칭 만들어보기

타입 별칭을 만들 때는 키워드 type을 사용한 문법을 적용합니다. 다음 예시를 한 번 살펴보세요.

Things라는 타입 별칭을 만들어 준 예시입니다. 이런 식으로 마치 변수 쓰듯이 타입 별칭을 만들어 두면 코드도 간소화되고, 수정도 쉽겠죠?

물론 일반 변수들과 구분을 하기 위해 작명에는 신경써줘야 합니다.

타입 별칭은 객체 데이터에도 얼마든지 적용 가능합니다.

위 쪽의 코드가 타입 별칭 없이 만든 객체(teacher1)이고, 아래가 타입 별칭을 적용한 객체(teacher2)입니다.

타입 별칭과 함수

타입스크립트에서는 함수 역시 타입 지정이 가능합니다. 함수에 들어갈 인자도 타입 지정을 하고, 함수의 결과값도 타입 지정을 하죠.

따라서 똑같은 맥락으로 함수를 사용할 때에도 타입 별칭을 쓸 수 있습니다.

데이터의 타입 지정과 비슷하죠? NumFunc라는 함수 타입을 만들었습니다.

이 NumFunc라는 타입은 인자로 숫자 두 개(x, y)를 받고 결과도 숫자로 반환합니다.

이를 바탕으로 두 인자를 서로 곱한 값을 반환하는 func1을 만든 예시입니다.

객체의 속성으로 존재하는 메서드들도 당연히 함수이기 때문에 위와 같은 방법으로 타입 별칭을 활용할 수 있겠습니다.

profile
Frontend Developer

0개의 댓글