러닝 타입스크립트 2장
포스트
취소

러닝 타입스크립트 2장

해당 포스트는 러닝 타입스크립트 2장을 읽고 정리한 포스트입니다.

🧮 타입

타입이란 JavaScript에서 다루는 값의 형태에 대한 설명입니다.
( 형태란 값에 존재하는 속성과 메서드 그리고 내장되어 있는 typeof 연산자의 결과를 의미 )

TypeScript는 값을 아래의 일곱 가지 기본 원시 타입으로 간주합니다.

  • 일곱 가지 기본 원시 타입
    1. null
    2. undefined
    3. boolean
    4. string
    5. number
    6. bigint
    7. symbol

Boolean, Number, String과 같은 래퍼 객체와는 다릅니다.

0️⃣ 타입 추론

기본적으로 초깃값으로 변수의 타입을 추론합니다.
(1)처럼 복잡한 초깃값도 제대로 추론합니다.

1
2
3
4
5
6
7
8
// myValue1: string
let myValue1 = "string";

// myValue2: number
let myValue2 = 2;

// (1) myValue3: string | number
let myValue3 = Math.random() > 0.5 ? "string" : 2;

1️⃣ 타입 시스템

값의 사용법이 타입과 일치하지 않으면 오류를 발생합니다.

1
2
3
4
5
let myValue1 = "2";
let myValue2 = 2;

myValue1.length;
myValue2.length; // Error: Property 'length' does not exist on type 'number'.

2️⃣ 오류 종류

noEmitOnError: false 옵션을 이용해서 구문/타입 오류를 무시하고 컴파일을 진행할 수 있습니다.
npx tsc --init으로 tsconfig.json을 생성하면 noEmitOnError: true가 기본 값입니다.
( 개인적인 생각으로는 구문 오류든 타입 오류든 다 해결하는 것이 좋다고 생각해서 굳이 바꿀 필요는 없다고 생각합니다. )

1
2
3
4
5
6
{
  "compilerOption": {
    "noEmitOnError": false, /* Disable emitting files if any type checking errors are reported. */
    // ... 생략
  }
}

1. 구문 오류

TypeScript가 코드로 이해할 수 없는 잘못된 구문을 감지할 때 발생합니다.
기본적으로 구문 오류가 발생하는 경우에는 컴파일을 중지합니다.
( 즉, JavaScript 파일을 생성하지 않습니다. )

1
2
// "JavaScript"에서도 같은 에러 발생
let let myValue; // Error: ','이(가) 필요합니다.

2. 타입 오류

TypeScript의 추론으로 타입에서 오류를 발견한 경우 발생합니다.
일반적으로 타입 오류가 발생했다고 컴파일을 중지하지는 않습니다.

개발자가 타입 체커에게 거짓말을 하지 않았다면 대부분은 런타임에서 오류가 발생하기 때문에 타입 오류를 해결하고 컴파일하는 것이 좋습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// myValue1: string
let myValue1 = "string";
// Error: 'toFixed' 속성이 'string' 형식에 없습니다. 'fixed'을(를) 사용하시겠습니까?
myValue1.toFixed();

// myValue2: number
let myValue2 = 2;
// Error: 'number' 형식에 'length' 속성이 없습니다.
myValue2.length;

// myValue3: string | number
let myValue3 = Math.random() > 0.5 ? "string" : 2;
// Error: 'string | number' 형식에 'toFixed' 속성이 없습니다. 'string' 형식에 'toFixed' 속성이 없습니다.
myValue3.toFixed();
// Error: 'string | number' 형식에 'length' 속성이 없습니다. 'number' 형식에 'length' 속성이 없습니다.
myValue3.length;

🎲 할당 가능성

이전에 초깃값을 기반으로 변수의 타입을 추론하는 것을 확인했습니다.
이전에 추론했던 타입이 재할당에도 그대로 유지되며 사용됩니다.

1
2
3
4
5
6
7
8
9
10
11
12
// myValue1: string
let myValue1 = "string";
myValue1 = "apple";

// myValue2: number
let myValue2 = 2;
myValue2 = 26;

// myValue3: string | number
let myValue3 = Math.random() > 0.5 ? "string" : 2;
myValue3 = myValue1;
myValue3 = myValue2;

단 변수를 const로 선언하면 기본적으로 재할당도 불가능하고 더 구체적인 타입으로 추론하기 때문에 TypeScript 측면에서도 재할당이 불가능합니다.

1
2
// const myValue1: "string"
const myValue1 = "string";

🩻 타입 애너테이션

변수에 초깃값을 작성하지 않는 경우가 있을 수 있습니다.
기본적으로는 any 타입으로 추론하게 됩니다.

0️⃣ 진화하는 any

any로 추론된 변수는 이후에 할당되는 값의 타입을 갖습니다.
( 즉 기존 any 타입에서 진화됩니다. )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// let myValue: any
let myValue;

// let myValue: any
myValue = 1;

// let myValue: number
myValue.length; // Error: Property 'length' does not exist on type 'number'.

// let myValue: number
myValue = "1";

// let myValue: string
myValue.toFixed(); // Error: Property 'toFixed' does not exist on type 'string'.

초깃값이 정해지지 않은 변수라면 타입 애너테이션을 이용해서 타입을 지정해줄 수 있습니다.
( 런타임 코드에는 전혀 영향을 주지 않고 컴파일 시 제거됩니다. )

1
2
3
4
5
6
7
8
// let myValue1: any
let myValue1;

// let myValue2: string
let myValue2: string;

myValue2 = "apple";
myValue2 = 26; // Error: 'number' 형식은 'string' 형식에 할당할 수 없습니다.

만약 타입 애너테이션을 지정하지 않고 초깃값을 정해주지 않으면 any가 할당되어 타입 체커의 도움을 받을 수 없으며, 런타임에 문제가 발생할 가능성이 높습니다.

1
2
3
4
5
6
let myValue: any;

myValue = "str";

// ( 런타임에 발생 ) Error: TypeError: myValue.toFixed is not a function
myValue.toFixed();

1️⃣ 불필요한 타입 애너테이션

만약 타입 체커에 의해 자동으로 추론되는 타입이라면 타입 애너테이션을 사용하지 않는 것이 좋습니다.
( 물론 상황에 따라 다르지만 일반적인 경우에 대한 이야기입니다. )

1
2
let myValue1: string = "apple";
let myValue2: number = 26;

2️⃣ noImplicitAny

TypeScript에서 any로 추론하는 것을 막고 싶다면 noImplicitAny 설정을 사용하면 됩니다.

1
2
3
4
5
6
{
  "compilerOption": {
    "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
    // ... 생략
  }
}

위와 같은 설정을 하면 (2)에서 매개변수의 타입을 정의하지 않았다는 타입 오류를 발생시킵니다.
( 근데 왜 (1)에서는 오류를 발생시키지 않을까요…? 🥲 )

1
2
3
4
5
6
7
8
// (1) 근데 이건 왜 에러가 안 나죠...?
let v;

v = 10;
v = "a";

// (2) Error: Parameter 'x' implicitly has an 'any' type.
const func = (x) => {};

🔭 타입 형태

타입이 정해졌다면 해당 타입이 가능한 행위를 쉽게 사용할 수 있도록 자동완성해주고, 잘못된 부분이 있는지 체크해줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 타입 애너테이션을 이용해서 구체적인 타입을 정해줘도 되지만 비효율적이고 가독성도 안 좋음
const champion1: {
  firstName: string;
  age: number;
} = {
  firstName: "Aatrox",
  age: 26,
};

const champion2 = {
  firstName: "Aatrox",
  age: 26,
};

// Error: 'firstname' 속성이 '{ firstName: string; age: number; }' 형식에 없습니다. 'firstName'을(를) 사용하시겠습니까?
champion2.firstname;

🍻 모듈과 스크립트

TypeScript에서는 ES6의 모듈 시스템인 ESM을 사용합니다.

  • 모듈: export | import가 있는 파일
  • 스크립트: 모듈이 아닌 모든 파일 ( export | import가 없는 파일 )

0️⃣ 모듈

모듈에서 선언 변수는 다른 파일과 충돌되지 않습니다.
즉, 각 파일에 종속적이고 export로 내보내지 않으면 다른 파일에서 사용할 수 없습니다.

아래 예시의 myValue는 각 파일의 독립적인 모듈로 존재하는 값입니다.
따라서 서로의 존재를 모르기 때문에 이름이 같아도 충돌나지 않습니다.

1
2
3
4
5
6
7
8
9
// a.ts
export const myValue = "Aatrox";

// b.ts
export const myValue = "Aatrox";

// index.ts
import { myValue } from "./a";
console.log(myValue);

1️⃣ 스크립트

스크립트에서는 동일한 변수가 다른 파일에 선언되어 있어도 충돌이 발생합니다.

아래 예시의 myValue는 각 파일이 달라도 해당 파일을 전역 스코프로 간주하기 때문에 같은 이름에 대한 충돌이 발생합니다.
( 솔직히 스크립트로 사용할 필요가 없다고 생각해서 즉, import, export 없이 사용할 이유가 없다고 생각해서 아래와 같은 충돌에 대해서는 신경쓰지 않아도 된다고 개인적으로 생각합니다. )

1
2
3
4
5
6
7
8
// a.ts
const myValue = "Aatrox"; // Error: 블록 범위 변수 'myValue'을(를) 다시 선언할 수 없습니다.

// b.ts
const myValue = "Aatrox"; // Error: 블록 범위 변수 'myValue'을(를) 다시 선언할 수 없습니다.

// index.ts
console.log(myValue); // undefined

충돌을 해결하는 방법은 스크립트를 모듈로 만들어주면 됩니다.
즉, 단순하게 원하는 것을 내보내거나 export {}처럼 아무것도 내보내지 않고 export를 사용하면 됩니다.

1
2
3
4
5
6
7
8
9
10
// a.ts
const myValue = "Aatrox";
export {};

// b.ts
const myValue = "Aatrox";
export {};

// index.ts
console.log(myValue); // "undefined"

📮 레퍼런스

  1. « 러닝 타입스크립트 2장 » ( 조시 골드버그 지음, 고승원 옮김, 한빛미디어, 2023 )
  2. TypeScript - Module
  3. inpa - TS 모듈 & 네임 스페이스 시스템 이해하기
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

Git Flow & GitHub Flow

자바스크립트 완벽 가이드 13장 정리 ( Asynchronous )