TypeScript - 데이터 타입

Solmii·2020년 8월 18일
3

TypeScript

목록 보기
1/2
post-thumbnail

TypeScript란?

TypeScript는 Microsoft에 의해 개발된 오픈소스 프로그래밍 언어이다.

참고로 우리 모두의 친구, VSCode도 TypeScript로 만들어졌다!

JavaScript는 브라우저의 동적 기능을 담당하기 위해, 넷스케이프사에서 10일만에 super super 유연하게 만들어진 언어이다.

처음 만들어졌을 당시만 해도 웹 브라우저는 HTML 위주 + JavaScript 쬐끔만 사용되었기 때문에 JavaScript의 이런 유연성을 활용해서 쉽게 뚝딱뚝딱🔨 어플리케이션을 개발할 수 있었다.

하지만, 점점 웹영역이 발전하고 프로젝트의 규모가 커지면서 개발자의 실수가 생기거나, 개발 단계에서는 잡히지 않던 버그가 배포후에 발견되는 등 여러가지 문제가 생겼다....

ES5에 strict mode가 추가되면서 여러가지 실수나 안전하지 않은 액션을 많이 방지해주긴 한다!!

type이 지정되지 않는 JavaScript의 특징때문에 여전히 대규모 프로젝트에서 JavaScript는 어렵고 불편한 언어였다.

이러한 불편함을 해결하기 위해 개발된게 바로 TypeScript!

TypeScript를 사용하면 Type이 지정되지 않는 JavaScript에 Type을 지정할 수 있게 된다.

🧐 이는 TypeScript를 설치할 때 같이 설치되는 TSC(TypeScript Compiler) 덕분인데,
이는 컴파일 과정에서 Type Check를 통해 에러 없이 안정성이 확보되면 지정해둔 Type들을 제거한 후 JavaScript 코드를 생성해준다.
즉, TypeScript라는 새로운 언어를 사용하는게 아니라 컴파일 단계에서 JavaScript 코드로 변환해주는 것!!


TypeScript 기본 타입

Type Script가 지원하는 데이터 타입을 말한다.

겉보기에는 다르게 생긴 녀석들이 있지만 사실 JavaScript와 거의 동일한 데이터 타입을 지원한다.

   숫자 (Number)   

우리가 아는 그 숫자 타입이다!

JavaScript와 마찬가지로 TypeScript의 숫자도 모두 부동 소수 값이다.

💁🏻‍♀️ 부동 소수란, 컴퓨터에서 2진수로 표현할 수 없는 소수를 근사한 값으로 변환하는 방법으로,
이름 그대로 소수점을 고정하지 않는 (floating point) 방식이다!
더 자세한 내용은 modolee님 이라는 분이 재밌게 잘 정리해주셔서 링크를 걸어둔다!

let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

이렇게 2진수, 8진수, 16진수, 10진수 리터럴를 지원한다.
(참고로 JavaScript는 기본적으로 10진수를 지원한다. 물론 다른 진수로 변환 가능!)


   문자열 (String)   

마찬가지로 JavaScript 문자열과 동일하다!

'" 는 물론, 백틱 ` 도 사용할 수 있다.


   불리언 (Boolean)   

JavaScript의 boolean과 같다. 참/거짓 true/false 값!


   배열 (Array)   

JavaScript처럼 값들을 배열로 다룰 수 있게 해주는데, 두가지 방법으로 작성한다.

let arr: number[] = [1, 2, 3]; // 타입뒤에 []를 쓰는 방법!
let list: Array<number> = [1, 2, 3]; // 제네릭 배열 타입을 쓰는 방법!

🤷🏻‍♀️ 제네릭 배열 타입?
Array<elemType> 이렇게 작성한다.
제네릭이란, 클래스 내부에서 사용하는 데이터의 타입을 외부에서 지정하는 것을 말한다.
yceffort님 블로그에 잘 정리되어있다.


   튜플 (Tuple)   

Tuple 이란, 요소의 타입과 개수가 고정된 배열을 말한다.

예를 들어, let tupleArr = [1, 'a', true]; 라는 배열을 만들고 싶다면

let tupleArr: [number, string, boolean]; // 이렇게 튜플 타입을 먼저 선언하고
tupleArr = [1, 'abc', true]; // 선언한 tupleArr 에 값을 넣는다.

tupleArr = [1, true, 'abc']; // 단, 선언한 튜플 타입의 데이터 순서와 다른 값을 넣으면 error!

인덱스로 접근해서 값을 확인할 수 있다!

console.log(tupleArr[0].substring(1)); // number 타입이라 substring을 확인할 수 없어서 error!
console.log(tupleArr[1].substring(1)); // 'bc'

   열거 (Enum)   

Enum 란, 값의 집합에 더 나은 이름을 붙여줄 수 있는 데이터 타입이다.

JavaScript에는 없는 개념인데다 설명도 혼란하다 혼란해....
무슨말인지 모르겠으니 바로 예시 ㄱㄱ

예를 들어 한국어, 영어, 일본어, 중국어중 한 언어를 제공하는 제품이 있다고 하면,

const productLanguage: 'ko' | 'en' | 'ja' | 'zh' = 'en';

이런식으로 작성할 수 있을 것이다.
(위 예시의 | 는 우선 or 로 이해하면 된다!)

이제 productLanguage 에는 정해진 언어 외에 다른 값을 지정하면 에러가 날 것이다.

하지만 개발을 하다 보니 제품마다 지원하는 언어가 다르다거나, 'ja''zh' 만으로 이게 일본어, 중국어를 의미하는 건지 한눈에 와닿지 않아 실수가 발생할 수 있다.

Enum 은 이런 경우에 사용할 수 있는데, 리터럴의 타입과 값에 각각 이름을 붙여서 코드의 가독성을 높여준다.

enum Language { // enum은 이렇게 작성하고..
  korean = 'ko',
  english = 'en',
  japanese = 'ja',
  chinese = 'zh',
  spanish = 'es',
}

const productLanguage: Language = Language.english // 또한 enum은 객체이므로 이렇게 접근해서 사용할 수 있다.

기본적으로, Enum 은 0부터 시작해서 1씩 증가하는 번호를 값들마다 매긴다.
(물론 수동으로 번호를 바꾸거나 값을 바꿀 수도 있다. 위의 예제가 수동으로 값을 바꾼 경우이다.)

enum Color {Red, Green, Blue}
let testEnum: Color = Color.Green; // 1

기본값으로, Red는 번호 0이 매겨져있기 때문에 testEnum 을 console로 확인해보면 1이 출력된다.

enum Color {Red = 2, Green, Blue}
let testEnum: Color = Color.Green; // 3

수동으로 Red의 번호를 2로 변경해보았더니, 이번에는 3이 출력된다!

💁🏻‍♀️ 한가지 유용한 팁은, 이렇게 매겨진 번호를 이용해 값의 이름을 알아낼 수 있다.

enum Color {Red = 2, Green, Blue}
let colorName: string = Color[3];
console.log(colorName); // Green

Color 에서 3 이라는 값과 매칭되는 Green 이 출력된다.

예시 출처 : TypeScript enum을 사용하는 이유


   Any   

TypeScript는 모든 데이터 타입을 명시해야 한다.

하지만, 개발을 하다보면 어떤 데이터 타입이 올지 알 수 없는 경우가 있다.
( ex : 사용자가 직접 input 창에 입력한 데이터 등 )

이렇게 타입 검사를 하지 않고, 어떤 값이 들어오든 컴파일에 통과하게끔 해야할 때 사용하는 것이 바로 Any 데이터 타입이다.

let anyDataOK: any = 26;
anyDataOK = "solmi"; // 200 OK!
anyDataOK = true; // 200 OK!

할당한 값의 타입이 변경되어도 문제없이 컴파일이 된다.

사실은, 어떻게 보면 Any 가 우리에게 가장 익숙한 데이터 타입이다.
기존에 JavaScript로 작업하던 것과 동일한 효과를 내기 때문😆!


Any 는 다른 데이터 타입과 결합했을 때도 유용하다.

예를들어 여러가지 타입이 섞인 배열을 관리해야 할 때 any[] 를 활용할 수 있다.

let anyArr: any[] = [1, 'a', 2, true];
console.log(anyArr); // [1, 'a', 2, true]

anyArr[1] = 20; // 값을 재할당할때도 유용하다. 분명 anyArr의 1번 인덱스는 'a' 라는 string이지만,
console.log(anyArr); // [1, 20, 2, true] 문제없이 20이 할당되었다!

   Void   

Void 는 쉽게 말해 Any 의 반대 타입이라고 생각하면 된다.

Any 와 달리 어떤 타입도 존재할 수 없음을 나타내기 때문인데, 보통 함수에서 return할 값이 없을 때 주로 사용된다.

function warnUser(): void {
    console.log('난 그냥 console만 출력하는 함수...');
}

const message: void = warnUser(); // '난 그냥 console만 출력하는 함수...'
console.log(message); // undefined -> 값을 반환하지 않는 함수는 실제로는 undefined를 반환하기 때문

물론 변수에도 Void 타입을 사용할 수 있지만, 그닥 유용하진 않다.

왜냐하면 Void 타입 변수는 undefined 만 할당할 수 있기 때문!!
( --strictNullChecks을 사용하지 않을 때는 null 도 할당할 수 있다. )

애초에 TypeScript에서 nullundefined 타입을 지원하는데 Void 를 변수에 선언하는건 정말 굳이...?


   Null, Undefined   

JavaScript와 같은 null undefined 가 아니다!!! 주의!!!

JavaScript에서 typeofnullundefined를 확인해보면

console.log(typeof null); // object
console.log(typeof null); // undefined

가 출력되던거 기억날것이다.

또한, 객체의 프로퍼티가 존재하지 않을때 JavaScript 자체에서 반환하는 일종의 에러메세지인 undefined 와 개발자가 명시적으로 할당할때의 undefined 가 다르기 때문에...
아무튼 JavaScript에서 undefined를 남발하면 대환장 파티가 열렸였다.


하지만 TypeScript에서의 nullundefined는 그 자체로 nullundefined 라는 고유한 타입을 가지고 있다.

TypeScript에서 객체에 프로퍼티를 할당하지 않으면, 소심하게 undefined를 반환하는게 아니라 바로 error를 때려버린다.

아무튼....TypeScript의 null, undefined

let u: undefined = undefined;
let n: null = null;

이렇게 사용할 수 있지만, 각각 undefined, null 외에 다른 값을 할당할 수 없기 때문에
(--strictNullChecks 가 true인 경우에만 )
유니언으로 다른 타입과 묶어서 사용하거나, 함수의 return 값 등에 주로 사용한다.


   Never   

Never 란, 절대 발생할 수 없는 값을 말한다.

보통 함수에서 항상 에러를 발생시키거나 절대 반환하지 않는 반환 타입으로 사용한다.

아래 예시처럼 빈배열을 타입으로 잘못 선언했을때도 Never 를 확인할 수 있다.

const test: [] = [];
test.push(3);


   객체 (Object)   

객체가 뭔지에 대한 설명은 스킵...!

JavaScript와의 차이점 위주로 알아보자면, 컴파일러 옵션에서 엄격한 타입 검사 stricttrue 로 설정하면, null 은 포함되지 않는다.
( null 도 typeof object 로 나와서 항상 if문 등으로타입 체크를 해야했던 JS에서의 고통 안녕...! )

또, 보다 정확하게 타입을 지정하기 위해 객체의 각 프로퍼티마다 개별적으로 타입을 지정할 수 있다.

let user: { name: string, age: number } = {
  name: 'solmi',
  age: 26
};

let errorUser: { name: string, age: number } = {
  name: 'solmi',
  age: '26', // error
  email: 'thesecon@gmail.com' // error
};

   타입 추론과 유니언, 인터섹션   

let ImNumber = 10;
ImNumber = '10';

const message = 'solmi';
const truely = true;

const numArr = [1, 2, 3];
const strArr = ['a', 'b', 'c'];

let test = 'hello';
test = 20;

위의 코드를 TypeScript를 통해 여러가지 타입을 지정해서 선언해보자!

let inferenceNum = 10;
inferenceNum = '10'; // error!

const message: string = 'solmi'; // string 타입
const truely: boolean = true; // boolean 타입

const numArr: number[] = [1, 2, 3]; // number 타입으로 된 array
const strArr: string[] = ['a', 'b', 'c']; // string 타입으로 된 array

let test: string | number = 'hello'; // string 또는 number 타입 - 유니언 타입
test = 20; // 그래서 숫자를 넣어도 200 OK!

기본적으로 타입을 명시하는 방법은 변수명 뒤에 : 콜론을 붙이고, 그 뒤에 타입을 작성해주면 된다.

첫줄에서 inferenceNum 의 경우, 타입을 명시하진 않고 10 이라는 숫자를 할당한 후에, 2번째 줄에 string 타입을 재할당하는 경우 에러가 뜬다.

이를 타입 추론(Inference) 이라고 하는데, 말 그대로 명시적으로 타입이 선언되지 않은 경우 타입을 추론해서 TypeScript가 자체적으로 지정해주는걸 의미한다.

즉, 첫 줄에서 10 이라는 number 타입 값을 할당한 순간, inferenceNum 은 number 타입으로 추론되었고, 따라서 두번째 줄에서 '10' 이라는 string 타입 값은 할당할 수 없기 때문에 에러가 발생한 것!

타입 추론과 타입 단언은 다음 글에서 더 이어서 설명하기로 하고 이정도에서 마무리!!!


마지막의 test 부분도 특이한데, |를 이용해서 string 또는 number 둘 다 허용해줬다.

이를 유니언이라고 하는데, 2개 이상의 타입을 허용하는 경우를 말한다.

| (vertical bar) 를 이용해서 구분하며, () 로 묶어줘도 되고 안묶어줘도 된다. (let union: (string | number))

유니언은 이외에도 Discriminating Union 이나 Mixin 등 다룰게 많아서 다음에 한번 더 정리할 예정!!


유니언과 반대되는 개념으로 인터섹션이 있는데 (& 를 이용해서 2개 이상의 타입을 조합하는 경우) 새로운 타입을 생성하지 않고 기존 타입을 조합할 수 있어서 유용하다.

💁🏻‍♀️ 유니언을 or(또는), 인터섹션을 and(그리고) 로 이해할 수 있다.


출처 : TypeScript Handbook (공식문서가 정말 잘되있다.), velopert님 블로그, sjk5766님 블로그, 한눈에 보는 타입스크립트

개발 왕초보 코린이입니다!
이 내용은 각종 강의&구글링을 통해 배운 내용을 정리하는 것으로, 제가 이해하고 넘어간 개념이 틀렸거나 더 보충할 개념이 있다면 댓글 남겨주시면 정말 감사하겠습니다!!

profile
하루는 치열하게 인생은 여유롭게

0개의 댓글