패스트 캠퍼스 MGS 3기 - 4월 29일(TypeScript 개요 및 타입)

JY·2022년 4월 29일
0

1. TypeScript 개요


  • 'Programming Language' 언어이다.
  • 'Compiled Language'이다.
    • 전통적인 'Compiled Language'와는 다른 점이 많다.
    • 그래서 'Transpile'이라는 용어를 사용하기도 한다.
  • 자바스크립트는 'Interpreted Language'이다.

컴파일 언어는 런타임에서 에러가 발생하기 전에 컴파일 타임에 미리 에러를 찾아서 고칠 수 있다.

CompiledInterpreted
컴파일 필요 ⭕컴파일 필요 ❌
컴파일러 필요 ⭕컴파일러 필요 ❌
컴파일하는 시점 ⭕(👉 컴파일 타임)컴파일하는 시점 ❌
컴파일된 결과물을 실행코드 자체를 실행
컴파일된 결과물을 실행하는 시점코드를 실행하는 시점 ⭕(👉 런타임)

타입스크립트는 노드나 브라우저 같은 런타임 환경에서 바로 읽어서 사용할 수 없다.
따라서 컴파일러를 통해 플레인 자바스크립트로 변경해주어야 한다.

2. TypeScript 설치 및 사용


자바스크립트 실행 환경 설치

👩‍💻 예제

컴파일러를 사용한 간단한 예제이다.

프로젝트(폴더)를 생성하고 해당 프로젝트에서 다음 명령어를 입력한다.

npm init -y
npm i typescript

tsconfig.json 파일을 생성하기 위해 다음 명령어를 입력한다.

npx tsc --init

프로젝트에 다음과 같은 파일이 생성되었다.

test.ts 파일을 생성한 후,

// test.ts

console.log('Hello')

다음 명령어로 컴파일한다.

npx tsc

js 파일이 생겼다!

🤔 npx tsc
프로젝트 안에 존재하는 모든 타입스크립트 파일을 전부 컴파일하려면 npx tsc를 입력하면 된다.
이때, 어떤 식으로 컴파일 할 것인지 설정 파일을 넣어주어야 한다. 설정 파일을 자동으로 생성해주는 디폴트 명령어이다.
이 명령어를 입력하면 파일(tsconfig.json)이 생성된다.

npm tsc --init

🤔 파일을 수정했을 때 자동으로 컴파일이 되도록 하는 법
매번 명령어를 입력해 수동으로 컴파일 할 필요 없이 다음 명령어를 통해 자동으로 컴파일 하도록 설정할 수 있다.

npx tsc -w

보통은 npx tsc로 바로 실행하지 않고, 현재 프로젝트의 어떤 스크립트가 돌아갈 수 있는지 등록한다. package.jsonscript를 다음처럼 수정한다.

  "scripts": {
    "build": "tsc"
  },

이제 다음 명령어를 사용해 tsc를 실행할 수 있다!

npm run build

🤔 VSCode에서 버전 선택

npm init -y
// 실제 런타임에 필요하지 않으므로 보통은 -D 플래그를 붙인다.
npm i typescript -D

3. Type Annotation


다음처럼 :을 사용해 특정한 변수나 객체에 타입을 지정하는 것을 'Type Annotation'이라고 한다.

let a: string;

// 가능
a = "Mark"

// 불가능
a = 39
let a: number;

// 불가능
a = "Mark"

// 가능
a = 39
function hello(b: number) {

}

// 가능
hello(39)

// 불가능
hello('Mark')

4. Type


  • Static Types: TypeScript, 개발하는 중간에 타입을 체크한다.
  • Dynamic Types: JavaScript, 개발 시엔 알 수 없고 런타임이 되어야 알 수 있다.

js에서는 throw를 사용해 런타임에서 에러를 체크하는 반면, ts에서는 이러한 과정이 필요없다. 개발 시에 에러를 체크할 수 있다.

  • ECMAScript 표준에 따른 기본 자료형 6가지

    • Boolean
    • Number
    • String
    • Null
    • Undefined
    • Symbol
    • Array: object 형
  • 프로그래밍을 도울 몇가지 타입이 더 제공

    • Any, Void, Never, Unknown
    • Enum
    • Tuple: object 형

Primitive Types

  • 오브젝트와 레퍼런스 형태가 아닌 실제 값을 저장하는 자료형이다.
  • 내장 함수를 사용할 수 있다.
    let name = 'mark';
    name.toString();
  • boolean, number, string, symbol, null, undefined가 해당된다.
  • 리터럴 값으로 프리미티브 타입의 서브 타입을 나타낼 수 있다.
    true;
    'hello';
    3.14;
    null;
    undefined;
  • 핵심 프리미티브 타입은 모두 소문자이다.

boolean

let isDone: boolean = false;
isDone = true;
console.log(typeof isDone)

👉 실행 결과

true 값을 가진 Boolean 래퍼 객체가 생성되면서 프리미티브 타입인 isNotOk 변수에 할당하려고 하지만 에러가 발생한다.

let isNotOk: boolean = new Boolean(true);

따라서 보통은 리터럴 값으로 프리미티브 타입을 사용한다.

let isOk: Boolean = true;

number

  • js와 같이 ts의 모든 숫자는 부동 소수점 값이다.
  • 16진수 및 10진수, 2진수, 8진수를 모두 지원한다.
  • NaN
  • _를 이용한 표기법
// 십진수
let decimal: number = 6;
// 16진수
let hex: number = 0xf00d;
// 이진수
let binary: number = 0b1010;
// 8진수
let octal: number = 0o744;
//NaN
let notANumber: number = NaN;
// _를 이용한 표기법
let underscoreNum: number = 1_000_000;

string

  • 다른 언어에서와 마찬가지로 텍스트 형식을 참조하기 위해 string 형식을 사용한다.
  • 문자열 데이터를 둘러싸기 위해 큰 따옴표(")나, 작은 따옴표(')를 사용한다.
let myName: string = "Mark";
myName = 'Anna';

Template String

  • ES6부터 추가된 기능으로, 행에 걸쳐 있거나 표현식을 넣을 수 있는 문자열이다.
  • 이 문자열은 백틱(`) 기호에 둘러쌓여 있다.
  • 포함된 표현식은 ${expr}와 같은 형태로 사용한다.
let fullName: string = 'Mark Lee';
let age: number = 39;
let sentence: string = `Hello, My name is ${ fullName }.

I'll be ${age + 1} years old next month.`;

console.log(sentence);

👉 실행 결과

symbol

  • new Symbol로 사용할 수 없다.
  • Symbol을 함수로 사용해서 symbol 타입을 만들어낼 수 있다.
console.log(Symbol("foo") === Symbol("foo")); // false

❓ 같은 함수이고, 같은 인자를 넣었지만 false가 나왔다.
그렇다면 symbol은 어디에 사용될까?

  • 프리미티브 타입의 값을 담아서 사용한다.
  • 고유하고 수정 불가능한 값으로 만들어준다.
  • 따라서 주로 접근을 제어하는 데 쓰는 경우가 많았다.
  • 함수로 사용 시엔 Symbol(대문자), 타입으로 사용 시엔 symbol(소문자)
const sym = Symbol();
const obj = {
  [sym]: "value",
};

// 접근 불가능
obj["sym"]

// 접근 가능
obj[sym]

null & undefined

  • 실제로 각각 nullundefined이라는 타입을 가진다.
  • 그 자체로는 그다지 유용하지 않다.
  • 둘다 소문자만 존재한다.
let u: undefined = undefined;
let n: null = null;

  • 설정을 하지 않으면 다른 모든 타입의 서브타입으로 존재한다.
    • numbernull 또는 undefined를 할당할 수 있다는 의미이다.
  • 하지만, 컴파일 옵션에서 --strictNullChecks를 사용하면, nullundefinedvoid나 자기 자신들에게만 할당할 수 있다. (보통은 이 옵션을 켜둔다.)
    • 이 경우, nullundefined를 할당할 수 있게 하려면, union type을 이용해야 한다.
// 불가능
let v: void = void;
let v: void = null;

// 가능
let v: void = undefined;

// union type
let union: string | null = null;
union = "Mark"

JS에서의 null

  • null이라는 값으로 할당된 것을 null이라고 한다.
  • 무언가가 있는데, 사용할 준비가 덜 된 상태이다.
  • null이라는 타입은 null이라는 값만 가질 수 있다.
  • 런타임에서 typeof 연산자를 이용해서 알아내면, object이다.

JS에서의 undefined

  • 값을 할당하지 않은 변수는 undefined이라는 값을 가진다.
  • 무언가가 아예 준비가 안 된 상태이다. (변수를 선언만 하고 값을 할당하지 않은 상태)
  • object의 property가 없을 때도 undefined이다.
  • 런타임에서 typeof 연산자를 이용해서 알아내면, undefined이다.

그 외 Types

--

object

  • 프리미티브 타입이 아닌 것을 나태내고 싶을 때 사용하는 타입이다.
  • non-primitive type: number, string, boolean, bigint, symbol, null, undefined가 아닌 경우

Array

  • 원래 js에서는 객체이다.
  • 공통 요소들의 모임이다.
  • 사용방법: Array<타입>, 타입[]
// 보통 위에 있는 방식을 더 많이 사용한다.
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];

let list: (number | string)[] = [1, 2, 3, "4"];

Tuple

  • 길이가 정해져있다.
  • 타입이 다를 수 있지만, 타입의 순서가 정확해야 한다.
let x: [string, number];

// 가능
x = ["hello", 24];
// 불가능, 순서가 같아야 한다.
x = [24, "Mark"];
// 불가능, 길이가 같아야 한다.
x[3] = "world";

// 가능
const person: [string, number] = ["Mark", 24];
// 불가능, 길이가 같아야 하므로 세 번째 요소 X
const [first, second, third] = person;

any

  • 어떤 타입이어도 상관없는 타입이다.

  • 최대한 쓰지 않는 것이 타입 시스템을 안전하게 유지할 수 있다.

    • 컴파일 타임에 타입 체크가 정상적으로 이뤄지지 않기 때문이다.
  • 컴파일 옵션 중 any를 써야 하는데 쓰지 않으면 오류를 뱉도록 하는 옵션이 있다. (nolmplicitAny)

  • 하지만 any를 사용할 수 밖에 없는 부분이 있다. 다음과 같은 경우, 로그만 찍고 있으므로 숫자나 문자라는 규정이 필요없다.

    function returnAny(message: any): any {
      console.log(message);
    }
  • any는 계속해서 개체를 통해 전파된다.

    let looselyTyped: any = [];
    const d = looselyTyped.a.b.c.d;
     // a, b, c 타입이 모두 any
    function leakingAny(obj: any) {
      const a = obj.num;
      const b = a + 1;
      return b;
    }
    
    const c = leakingAny({ num: 0 });
    c.indexOf("0");
    
    // a, b, c 타입이 모두 number로 누수를 막음
    function leakingAny(obj: any) {
      const a: number = obj.num;
      const b = a + 1;
      return b;
    }
    
    const c = leakingAny({ num: 0 });
    c.indexOf("0");

unknown

  • 응용 프로그램 작성 시 모르는 변수의 타입을 묘사해야 할 수 있다. 이 경우, 컴파일러와 미래의 코드를 읽는 사람에게 이 변수가 무엇이든 될 수 있음을 알려주는 타입이다.
  • any와 짝으로, any보다 타입-세이프하다.
    • any처럼 아무거나 할당할 수 있다.
    • 컴파일러가 타입을 추론할 수 있도록 타입의 유형을 좁히면 사용할 수 있다.
    • 하지만 타입을 확정해주지 않으면 다른 곳에 할당할 수 없고, 사용할 수 없다.
// 타입가드, maybe에 'true'라는 타입을 지정한다.
if (maybe === true) {
  // 'true'이므로 boolean은 가능
  const aBoolean: boolean =  maybe;
  // string은 불가능
  const aString: string = maybe;
}

// 타입가드, maybe에 'string'이라는 타입을 지정한다.
if (typeof maybe === 'string') {
  // 'string'이므로 string은 가능
  const aString: string = maybe;
  // boolean은 불가능
  const aBoolean: boolean = maybe;
}

never

  • 일반적으로 return에 사용된다.
  • 모든 타입의 서브타입이며, 모든 타입에 할당할 수 있다.
  • 하지만 never에는 그 어떤 것도 할당할 수 없다.
    • any 조차도 never에게 할당할 수 없다.
  • 잘못된 타입을 넣는 실수를 막고자 할 때 사용하기도 한다.


void

  • 어떤 타입도 가지지 않는
  • 대부분 함수의 반환 타입 때 사용된다.
  • 유일하게 undefinedreturn 할 수 있다.


5. Type System


  • 타입을 명시적으로 지정할 수 있다.
  • 타입을 명시적으로 지정하지 않으면, ts 컴파일러가 자동으로 타입을 추론한다.

작성자와 사용자 관점에서 코드 바라보기



in JavaScript

타입이란 해당 변수가 할 수 있는 일을 결정한다.

함수 사용법에 대한 오해를 야기한다.

in TypeScript

ts의 추론에 의지하는 경우 다음과 같은 문제가 발생한다.
이때, nolmplicitAny 옵션을 사용할 수 있다.

🤔 nolmplicitAny 옵션
타입을 명시적으로 지정하지 않은 경우, ts가 추론 중 any라고 판단하게 되면 컴파일 에러를 발생시켜 명시적으로 지정하도록 유도하는 옵션이다.


또한, 리턴 타입을 지정하지 않아 number로 추론된 경우 다음과 같은 문제가 발생한다.
이때, strictNullChecks 옵션을 사용할 수 있다.

🤔 strictNullChecks 옵션
모든 타입에 자동으로 포함되어 있는 nullundefined를 제거해주는 옵션이다.


그렇다면 명시적으로 리턴 타입을 지정해야 할까?
명시적으로 지정한 타입이 일치하지 않아 컴파일 에러가 발생하는데 이때, nolmplicitReturns 옵션을 사용할 수 있다.

🤔 nolmplicitReturns 옵션
함수 내에서 모든 코드가 값을 리턴하지 않으면, 컴파일 에러를 발생시키는 옵션이다.


🤔 나만의 타입 만들기


profile
🙋‍♀️

0개의 댓글