From a7e88b021a25e8e273ce856a6ecb9b7c6e376faf Mon Sep 17 00:00:00 2001 From: doitchuu Date: Sun, 16 Mar 2025 19:18:27 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[Add]=20=ED=83=80=EC=9E=85=20=EA=B3=84?= =?UTF-8?q?=EC=B8=B5=20=EA=B5=AC=EC=A1=B0=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/CH03/typescript_structure.jpeg | Bin 0 -> 31256 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/CH03/typescript_structure.jpeg diff --git a/assets/CH03/typescript_structure.jpeg b/assets/CH03/typescript_structure.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..a4c146684ab58f8a8957d0fc9fb38965764adae5 GIT binary patch literal 31256 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 U8q;*13F#-bM^NPoMc9k~0)l}PxBvhE literal 0 HcmV?d00001 From 4621a107264224e61528dfa5ba7b60e51a79f0f6 Mon Sep 17 00:00:00 2001 From: doitchuu Date: Sun, 16 Mar 2025 20:05:54 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[Docs]=203.1=20=ED=83=80=EC=9E=85=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=EB=A7=8C=EC=9D=98=20=EB=8F=85?= =?UTF-8?q?=EC=9E=90=EC=A0=81=20=ED=83=80=EC=9E=85=20=EC=8B=9C=EC=8A=A4?= =?UTF-8?q?=ED=85=9C=20=EC=A0=95=EB=A6=AC=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seulgi.md" | 423 +++++++++++++++++- 1 file changed, 422 insertions(+), 1 deletion(-) diff --git "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" index 3beced8..9e724ba 100644 --- "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" +++ "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" @@ -1 +1,422 @@ - +# 타입스크립트만의 독자적 타입 시스템 + +타입스크립트는 자바스크립트의 자료형에서 제시하지 않은 독자적인 타입 시스템을 가지고 있다. + +자바스크립트의 슈퍼셋으로 정적 타이핑을 할 수 있는 타입스크립트가 등장하면서 비로소 타입스크립트의 타입 시스템이 구축되었다.
모든 타입 시스템은 타입스크립트에만 존재하는 키워드지만,
그 개념은 자바스크립트에 기인한 타입 시스템이라는 걸 인지하고 타입을 살펴봐야 한다. + +타입스크립트 타입 계층 구조 + +
+
+ +## 1) any 타입 + +any 타입은 모든 값을 오류 없이 받을 수 있다. 자바스크립트에서의 기본 사용 방식과 같으므로 타입을 명시하지 않은 것과 동일한 효과를 나타낸다. + +타입스크립트는 동적 타이핑 특징을 가진 자바스크립트에 정적 타이핑을 적용하는 것이 주된 목적이지만 any타입은 이러한 목적을 무시할 수 있다. + +### tsconfig.json 파일에서 any에 대한 경고를 설정할 수 있다! + +tsconfig.json 파일에서 **noImplicitAny** 옵션을 활성화하면 타입이 명시되지 않은 변수의 암묵적인 any 타입에 대한 경고를 발생시킬 수 있다. + +### any 타입을 어쩔 수 없이 사용해야 할 때를 알아보자! + +**(1) 개발 단계에서 임시로 값을 지정해야 할 때** + +추후 값이 변경될 가능성이 있거나 아직 세부 항목에 대한 타입이 확정되지 않은 경우, any로 지정해 타입을 세세하게 명시하는 데 소요되는 시간을 절약할 수 있다. + +**(2) 어떤 값을 받아올지 또는 넘겨줄지 정할 수 없을 때** + +API 요청 및 응답 처리, 콜백 함수 전달, 타입이 잘 정제되지 않아 파악이 힘든 외부 라이브러리 등을 사용할 때 어떤 인자를 주고받을지 특정하기 힘들 때가 있다. + +이처럼 주고받을 값이 명확하지 않을 때 any타입을 선언해야 할 수 있다. + +```ts +type ModalParams = { + show: boolean; + // ... + action?: any; +}; +``` + +- action 속성은 모달 창을 그릴 때 실행될 함수이다. +- 다양한 범주의 액션에 따라 인자의 개수나 타입을 일일이 명시하기 힘들 수 있다. + +> 이때 any 타입을 사용해 다양한 액션 함수를 전달할 수 있다. + +**(3) 값을 예측할 수 없을 때 암묵적으로 사용** + +브라우저의 `Fetch API`처럼 다양한 값을 반환하는 API가 있다. + +`Fetch API` 일부 메서드는 요청 이후 응답을 특정 포맷으로 파싱하는데 이때 반환 타입이 any로 매핑되어 있다. + +```ts +async function load() { + const response = await fetch("https://api.com"); + const result = await response.json(); + + return result; +} +``` + +> response.json()의 리턴 타입은 Promise로 정의되어 있다. + +**그럼에도 되도록이면 any타입은 지양하는 것이 좋다. 타입 검사를 무색하게 만들고 잠재적으로 위험한 상황을 초래할 수 있다.** + +
+
+ +## 2) unknown 타입 + +unknown 타입은 모든 타입의 값이 할당될 수 있는 타입으로 타입스크립트 3.0이 릴리즈 될 때 추가되었다. + +### any와의 차이점을 알아보자! + +| 비교사항 | any | unknown | +| ----------------------------------- | --- | ---------------------- | +| 어떤 타입이든 해당 타입에 할당 가능 | O | O | +| 해당 타입을 다른 타입으로 할당 가능 | O | X (any 타입 외 불가능) | + +```ts +let unknownValue: unknown; + +unknownValue = "100"; // ✅ 함수, 숫자, 문자열 다 할당 가능 +unknownValue = 100; // ✅ + +let a: any = unknownValue; // ✅ any 타입으로 선언한 변수를 제외한 다른 변수 모두 할당 불가 +let b: string = unknownValue; // ❌ +``` + +### any타입과 비슷한데 왜 unknown 타입이 추가되었을까? + +```ts +const aFunction: unknown = () => console.log("a"); +aFunction(); // ❌ 'aFunction' is of type 'unknown'. +``` + +위의 예시처럼 함수를 unknown타입 변수에 할당할 때는 컴파일러가 경고를 주지 않는다.
+실행하면 에러가 발생하는데 함수뿐만 아니라 객체 속성 접근,
+클래스 생성자 호출을 통한 인스턴스 생성 등 객체 내부에 접근하는 모든 시도에서 에러가 발생한다. + +### 왜 호출 시 문제가 생길까? + +unknown 타입으로 할당된 변수는 어떤 값이든 올 수 있음을 의미하는 동시에 개발자에게 엄격한 타입 검사를 강제하는 의도를 담고 있다. any 타입으로 사용할 경우 런타임에 예상치 못한 버그가 발생할 가능성이 높아지는데 unknown 타입은 이런 상황을 보완한다. + +### any보다 어떤 점이 더 좋을까? + +- any타입과 유사하지만 타입 검사를 강제한다. +- 타입이 식별된 후에 사용할 수 있어 더 안전하다. +- 데이터 구조를 파악하기 힘들 때는 unknown 타입으로 대체해서 사용하는 방법이 권장된다. + +> 다만 어떤 값을 받을 지 모르는 상황에서 unknown을 사용하면 가공할 때 타입 캐스팅을 모두 해야 하는 상황이 생길 수 있다. (자바스크립트에서 타입스크립트로 변환 시도 동일) + +### 또, 어떨 때 unknown 타입을 활용할 수 있을까? + +```ts +const env = process.env as unknown as ProcessEnv; +``` + +위처럼 강제 타입 캐스팅을 통해 타입을 전환할 때 사용한다. + +혹은, 타입스크립트 4.4부터 try-catch 에러의 타입이 any에서 unknown으로 변경되서 에러 핸들링을 할 때도 unknown을 사용할 수 있다. + +다만 `as unknown as Type` 같이 강제 타입 캐스팅을 하기도 하는데 any와 다를게 없어 지양해야 한다. + +
+
+ +## 3) void 타입 + +함수에 전달되는 매개변수의 타입과 반환하는 타입을 지정해야 하는데 타입스크립트에서는 함수가 어떤 값을 반환하지 않는 경우는 void로 지정하여 사용한다. + +- void 타입은 undefined가 아니다. +- 함수에 국한된 타입은 아니다. +- 변수에도 할당할 수 있지만 함수가 아닌 값에 대해선 대부분 무의미하다. +- void로 할당된 변수는 undefined 또는 null값만 할당할 수 있다. +- 명시적인 의미를 부여하는 관점에서 undefined와 null 타입 키워드를 직접 사용해 타입을 지정하는 것이 더 바람직하다. +- 일반적으로 함수 자체를 다른 함수 인자로 전달하는 경우가 아니면 void 타입은 잘 명시하지 않는다.(알아서 컴파일러가 추론하기 때문) + +
+
+ +## 4) never 타입 + +일반적으로 함수 관련해서 많이 사용되는 타입으로, 값을 반환할 수 없는 타입을 말한다. 여기서 값을 반환하지 않는 것과 반환할 수 없는 것을 명확히 구분해야 한다. + +### 자바스크립트에서 값을 반환할 수 없는 예시를 알아보자! + +**(1) 에러를 던지는 경우** + +자바스크립트는 런타임에 의도적으로 에러를 발생시키고 캐치할 수 있다. + +throw 키워드를 사용해 에러를 발생시킬 때, 이는 값을 반환하는 것으로 간주하지 않는다. 특정 함수가 실행 중 마지막에 에러를 던지는 작업을 수행한다면 해당 함수의 반환 타입은 never이다. + +```ts +function generateError(res: Response): never { + throw new Error(res.getMessage()); +} +``` + +**(2) 무한히 함수가 실행되는 경우** + +함수 내에서 무한 루프를 실행하는 경우 함수가 종료되지 않음을 의미하기 때문에 값을 반환하지 못한다. + +### never에 다른 특징을 알아보자! + +never 타입은 모든 타입의 하위 타입으로 never 자신을 제외한 어떤 타입도 never 타입에 할당될 수 없다.
+조건부 타입을 결정할 때 특정 조건을 만족하지 않는 경우, 엄격한 타입 검사 목적으로 명시적으로 사용하기도 한다. + +
+
+ +## 5) Array 타입 + +배열 타입을 나타내는 Array 키워드는 자바스크립트에서도 `Object.prototype.toString.call(...)` 연산자를 사용해 확인할 수 있다. + +`Object.prototype.toString.call(...)`은 객체 타입을 알아내는 데 사용하는 함수로, typeof의 경우 객체 타입을 단순히 object 타입으로 알려주지만 `Object.prototype.toString.call(...)` 함수는 객체의 인스턴스까지 알려준다. + +```ts +const arr = []; +console.log(Object.prototype.toString.call(arr)); // [object Array] +``` + +### 왜 타입스크립트에서 배열을 다뤄야할까? + +1. 자바스크립트에서는 배열은 객체에 속하는 타입이다. (배열을 단독 배열이라는 자료형에 국한하지 않는다.) +2. 타입스크립트에서 Array 타입을 사용하기 위해 특수한 문법을 함께 다뤄야 한다. + +### 배열보다 더 좁은 범위인 튜플(Tuple)도 있다! + +배열은 Array 키워드와 대괄호([])를 사용해 직접 타입을 명시할 수 있는데, 이때 타입은 배열보다 더 좁은 범위인 튜플이다. + +### 다른 언어에서는 배열을 어떻게 다룰까? + +타입스크립트 뿐만 아니라 자바, C++과 같은 정적 언어에서는 배열의 원소로 하나의 타입만 사용하도록 명시한다. + +배열을 선언할 때 크기까지 제한하기도 하는데 타입스크립트에서는 배열의 크기까지 제한하지 않지만
+정적 특성을 살려 명시적인 타입을 선언하여 **해당 타입의 원소를 관리하는 것을 강제**한다. + +```ts +const arr: number[] = [1, 2, 3]; // 숫자 원소만 허용 +const arr: Array = [1, 2, 3]; // 위와 도일한 타입 +``` + +- 자료형 + 대괄호([]) 형식으로 배열 타입 선언 +- 제네릭 방식으로 배열 타입 선언 + +> 두 방식의 차이점은 선언 형식 외에는 없다. + +```ts +const arr1: Array = [1, "1"]; +const arr2: number[] | string[] = [1, "1"]; +const arr3: (number | string)[] = [1, "1"]; +``` + +여러 타입을 모두 관리해야 하는 배열을 선언하려면 유니언 타입을 사용하면 된다. + +### 배열 기능에 길이 제한을 추가하려면? + +튜플이라는 배열 하위 타입으로 길이 제한을 추가하면 된다. 대괄호 안에 타입 시스템을 기술하고, 대괄호 안에 선언하는 타입 개수가 튜플이 가질 수 있는 원소의 개수이다. + +즉, 튜플은 배열의 특정 인덱스에 정해진 타입을 선언하는 것과 같다. 튜플을 이용해 사전에 허용하지 않은 타입이 서로 섞이는 것을 방지해 타입 안정성을 제공한다. + +```ts +let tuple: [number] = [1]; // ✅ +tuple = [1, 2]; // ❌ +tuple = [1, "1"]; // ❌ + +let tuple: [number, string, boolean] = [1, "1", true]; // ✅ +``` + +> 튜플의 경우 컨벤션을 잘 지키고 각 배열 원소의 명확한 의미와 쓰임을 보장할 때 더욱 안전하게 쓸 수 있는 타입이다. + +### 리액트 훅을 예시로 튜플 사용 예시를 살펴보자! + +리액트 16.8 버전부터 도입된 훅이라는 요소 중 useState는 튜플 타입을 반환한다. + +1. 첫 번째 원소는 훅으로 부터 생성 및 관리되는 상태 값을 의미한다. +2. 두 번째 원소는 해당 상태를 조작할 수 있는 세터를 의미한다. + +> useState API는 배열 원소의 자리마다 명확한 의미를 부여하기 때문에 컴포넌트에서 사용하지 않은 값에 접근하는 오류를 방지할 수 있다. 또 구조 분해 할당을 사용해 사용자가 자유롭게 이름을 정의할 수 있다. + +```ts +import { useState } from "react"; + +const [value, setValue] = useState(false); +``` + +### 튜플과 배열의 성질을 혼합해 사용할 수 있다! + +스프레드 연산자(...)를 사용해 특정 인덱스에서 요소를 명확한 타입으로 선언할 수 있다.
+이후 나머지 인덱스에서는 배열처럼 동일한 자료형의 원소를 개수 제한 없이 받도록 할 수 있다. + +```ts +const example: [number, string, ...number[]] = [1, "1", 2, 3, 4]; +``` + +### 옵셔널 프로퍼티(선택적 속성)를 명시하고 싶을 때는? + +물음표(?) 기호와 함께 해당 속성을 선언할 수 있다. 해당 원소는 옵셔널하기 때문에 해당 인덱스에 필수적으로 자리 잡고 있지 않음을 의미한다. + +> **옵셔널(optional)?**
특정 속성 또는 매개변수가 값이 있을 수도 없을 수도 있는 것을 의미한다. 선택적 속성은 타입스크립트에서 좀 더 유연한 데이터 모델링과 사용자 정의 타입을 지원하기 위한 개념이다. + +```ts +const tuple1: [number, number?] = [1, 2]; +const tuple2: [number, number?] = [1]; +``` + +
+
+ +## 6) enum 타입 + +enum 타입은 열거형으로 부르는데 일종의 구조체를 만드는 타입 시스템이다. + +- enum 사용으로 열거형을 정의할 수 있는데 열거형은 각각의 멤버를 가지고 있다. +- 타입스크립트는 명명한 각 멤버의 값을 스스로 추론한다. +- 기본적인 추론 방식은 숫자 0부터 1씩 늘려가며 값을 할당하는 것이다. + +```ts +enum Names { + kim, // 0 + lee, // 1 + park, // 2 + chu, // 3 + oh, // 4 +} + +// 각 멤버 접근 방식은 객체 속성 접근 방식과 동일 +Names.chu; // 3 +Names["chu"]; // 3 + +// 역방향 접근도 가능하다 +Name[3]; // "chu" +``` + +### 각 멤버에 명시적 값 할당도 가능하다! + +명시적 값을 할당할 수도 있지만 일부 멤버에 값을 직접 할당하지 않아도
타입스크립트는 누락된 멤버를 이전 멤버 값의 숫자 기준으로 1씩 늘려가며 자동으로 할당한다. + +```ts +enum Names { + kim = "kim", + lee = "lee", + park = 100, + chu, // 101 + oh, // 102 +} +``` + +### 주로, enum은 언제 쓰일까? + +문자열 상수를 생성하는 데 사용해 응집력있는 집합 구조체를 만들 수 있다. +이를 통해 사용자 입장에서도 간편하게 활용하고, 열거형 그 자체로 변수 타입으로도 지정할 수 있다. + +이때, 열거형을 타입으로 가지는 변수는 **해당 열거형이 가지는 모든 멤버를 값으로 받을 수 있다.**(코드의 가독성을 높여주는 효과!) + +### 아래 예시에서 enum을 사용해 얻을 수 있는 효과를 문자열 타입 지정과 비교해보자! + +```ts +enum StatusType { + HOLD = "HOLD", + READY = "READY", + DELIVERING = "DELIVERING", + DELIVERED = "DELIVERED", +} + +const checkItemStatus = (itemStatus: StatusType) => { + switch (itemStatus) { + case StatusType.HOLD: + case StatusType.READY: + case StatusType.DELIVERING: + return false; + + case StatusType.DELIVERED: + default: + return true; + } +}; +``` + +1. 타입 안정성: StatusType에 명시되지 않은 다른 문자열은 인자로 받을 수 없다. +2. 명확한 의미 전달과 높은 응집력 : StatusType이 다루는 값이 명확하고, 상태에 대한 값을 모아있는 것이라 응집력이 높다. +3. 가독성: 응집도가 높기 때문에 말하는 바가 명확하다. 열거형 멤버를 통해 어떤 상태를 나타내는 지 쉽게 이해 가능하다. + +### 주의할 점은 없을까? + +숫자로만 이루어져 있거나 타입스크립트가 자동으로 추론한 열거형은 안전하지 않은 결과를 낳을 수 있다.또, 역방향으로 접근하더라도 타입스크립트는 이를 막지 않는다. + +### 그럴 때 const enum을 사용한다! + +역방향 접근을 허용하지 않아 자바스크립트에서의 객체에 접근하는 것과 유사한 동작을 보장하지만
숫자 상수로 관리되는 열거형은 선언한 값 이외의 값을 할당하거나 접근할 때 이를 방지하지 못한다. + +반면 문자열 상수 방식으로 선언한 열거형은 미리 선언하지 않은 멤버로 접근을 방지할 수 있어
문자열 상수 방식으로 열거형을 사용하는 것이 숫자 상수 방식보다 안전한다. + +의도하지 않은 값의 할당이나 접근을 방지하는 데 도움이 된다. + +```ts +const enum NUMBER { + ONE = 1, + TWO = 2, +} + +const num: NUMBER = 100; // Error! (확인 필요) + +const enum STRING { + ONE = "1", + TWO = "2", +} + +const strNum: STRING = "3"; // Error! +``` + +### 이외에도 열거형의 가장 큰 문제가 있다! + +타입스크립트 코드가 자바스크립트 코드로 변환될 때 `즉시 실행 함수(IIFE) `형식으로 변환된다. + +```ts +enum Direction { + Up, + Down, + Left, +} +``` + +```js +"use strict"; +var Direction; +(function (Direction) { + Direction[(Direction["Up"] = 0)] = "Up"; + Direction[(Direction["Down"] = 1)] = "Down"; + Direction[(Direction["Left"] = 2)] = "Left"; +})(Direction || (Direction = {})); +``` + +일부 번들러에서 트리쉐이킹 과정 중 즉시 실행 함수로 변환된 값을 사용하지 않는 코드로 인식하지 못하는 경우가 발생할 수 있어 불필요한 코드의 크기가 증가하는 결과가 생길 수 있다. + +이런 문제를 해결하기 위해 `const enum` 또는 `as const assertion`을 사용해 유니온 타입으로 열거형과 동일한 효과를 얻는 방법이 있다. + +### as const assertion으로 활용해보자! + +```ts +const Color = { + RED: "red", + BLUE: "blue", +} as const; + +const color: (typeof Color)[keyof typeof Color] = Color.RED; + +console.log(color); +``` + +```js +"use strict"; +const Color = { + RED: "red", + BLUE: "blue", +}; +const color = Color.RED; +console.log(color); +``` + +Color는 단순한 객체로 변환되어 ✅ 트리 쉐이킹이 가능하다. From b3b45d02d45cdfcc26ee60557b9e17ad7ea86b07 Mon Sep 17 00:00:00 2001 From: doitchuu Date: Sun, 16 Mar 2025 23:25:52 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[Docs]=20=EB=82=B4=EC=9A=A9=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seulgi.md" | 2 ++ 1 file changed, 2 insertions(+) diff --git "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" index 9e724ba..7769f6a 100644 --- "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" +++ "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" @@ -370,6 +370,8 @@ const enum STRING { const strNum: STRING = "3"; // Error! ``` +> 현재 코드에서는 `const num: NUMBER = 100;` 해당 부분도 동일하게 에러가 난다. 왜 에러가 나는 지 깊이 파보자!! + ### 이외에도 열거형의 가장 큰 문제가 있다! 타입스크립트 코드가 자바스크립트 코드로 변환될 때 `즉시 실행 함수(IIFE) `형식으로 변환된다. From 219f6a9194d4ed95142d3ad91379322c7c2542eb Mon Sep 17 00:00:00 2001 From: doitchuu Date: Mon, 17 Mar 2025 03:58:32 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[Docs]=203.2=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=A1=B0=ED=95=A9=20=EC=A0=95=EB=A6=AC=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seulgi.md" | 360 +++++++++++++++++- 1 file changed, 359 insertions(+), 1 deletion(-) diff --git "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.2_\355\203\200\354\236\205_\354\241\260\355\225\251/seulgi.md" "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.2_\355\203\200\354\236\205_\354\241\260\355\225\251/seulgi.md" index 3beced8..6b4fab6 100644 --- "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.2_\355\203\200\354\236\205_\354\241\260\355\225\251/seulgi.md" +++ "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.2_\355\203\200\354\236\205_\354\241\260\355\225\251/seulgi.md" @@ -1 +1,359 @@ - +# 타입 조합 + +타입 검사 심화 버전을 알아보자! + +
+
+ +## 1) 교차 타입(intersection) + +교차 타입을 사용하면 여러 가지 타입을 결합해 하나의 단일 타입으로 만들 수 있다.
+기존에 존재하는 다른 타입을 합쳐 모든 멤버를 가지는 새로운 타입을 생성하는 것이다. + +- 교차 타입은 &을 사용해서 표기한다. +- 결과물로 탄생한 단일 타입에는 타입 별칭을 붙일 수 있다. +- 여러 개의 타입을 교차시킬 수도 있다. + +```ts +type Me = { + name: string; + age: number; + location: string; + isWork: boolean; +}; + +type MyInfo = Me & { address: string }; +``` + +> Me 타입의 변수를 선언하고 값을 할당하면 Me타입의 모든 멤버와 address까지 가지게 된다. + +
+
+ +## 2) 유니온 타입(union) + +유니온 타입은 타입 A 또는 타입 B 중 하나가 될 수 있는 타입이며 A | B 같이 표기한다. +주로 특정 변수가 가질 수 있는 타입을 전부 나열하는 용도로 사용된다. + +교차 타입과 마찬가지로 2개 이상의 타입을 이어 붙일 수 있고, +타입 별칭을 통해 중복을 줄일 수 있다. + +```ts +type Me = { + name: string; + age: number; + location: string; + isWork: boolean; +}; + +type You = { + name: string; + age: number; + location: string; + areMarried: boolean; +}; + +type Info = Me | You; + +const printInfo = (info: Info) => { + console.log(info.location); + console.log(info.areMarried); // Error! +}; +``` + +> 해당 함수 내부에서 areMarried 속성을 참조하려고 하면 에러가 발생하는데, 이는 areMarried가 한 속성에만 존재하기 때문이다. + +### 맨 앞에 & 혹은 |를 붙이는 경우를 알아보자! + +교차 타입과 유니온 타입은 여러 줄에 걸쳐 표기할 수 있는데,
+이럴 때 각 줄의 맨 앞에 & 혹은 |를 붙여서 표기하면 된다. + +
+
+ +## 3) 인덱스 시그니처(Index Signatures) + +인덱스 시그니처는 특정 타입의 속성 이름을 알 수 없지만 속성값의 타입을 알고 있을 때 사용하는 문법이다. + +인터페이스 내부에 `[Key: K]: T` 꼴로 타입을 명시한다. 이는 해당 타입의 속성 키는 모두 K타입이어야 하고 속성값은 모두 T타입을 가져야한다는 뜻이다. + +```ts +interface IndexSignature1 { + [key: string]: number; +} + +interface IndexSignature2 { + [key: string]: number; + length: number; + isValid: boolean; + name: string; // Error! +} +``` + +> 인덱스 시그니처를 선언할 때 다른 속성을 추가로 명시해줄 수 있는데 이때 추가로 명시된 속성은 인덱스 시그니처에 포함되는 타입이어야 한다. + +
+
+ +## 4) 인덱스드 엑세스 타입(Indexed Access Types) + +인덱스드 엑세스 타입은 다른 타입의 특정 속성이 가지는 타입을 조회하기 위해 사용된다.
+인덱스에 사용되는 타입 또한 그 자체로 타입이기 때문에 유니온 타입, keyof, 타입 별칭 등의 표현을 사용할 수 있다. + +```ts +type Example = { + a: number; + b: string; + c: boolean; +}; + +type IndexedAccess = Example["a"]; // 인덱스드 엑세스 타입 +type IndexedAccess1 = Example[keyof Example]; // number | string | boolean +``` + +배열의 요소 타입을 조회하기 위해 인덱스드 엑세스 타입을 사용하는 경우가 있다. + +1. number로 인덱싱해 배열 요소를 얻는다. +2. typeof 연산자를 붙여준다. +3. 해당 배열 요소의 타입을 가져올 수 있다. + +```ts +const People = [ + { name: "a", age: 1 }, + { name: "b", age: 2 }, + { name: "c", age: 3 }, +]; + +type PeopleOf = (typeof People)[number]; + +// type People = { name: string; age: number } +type PeopleType = PeopleOf; +``` + +> 해당 예시는 현재 에러가 난다. (확인 필요) + +
+
+ +## 5) 맵드 타입(Mapped Types) + +자바스크립트의 map은 배열 A를 기반으로 한 타입을 선언할 때 사용하는 문법인데, 인덱스 시그니처 문법을 사용해서 반복적인 타입 선언을 효과적으로 줄일 수 있다. + +```ts +type Example = { + a: number; + b: string; + c: boolean; +}; + +type Subset = { + [K in keyof T]?: T[K]; +}; + +const aExample: Subset = { a: 3 }; +const bExample: Subset = { a: 4, c: true }; +``` + +- 맵드 타입에서 매핑할 때는 readonly와 ?를 수식어로 적용할 수 있다. +- 맵드 타입의 특이한 점은 수식어 제거도 가능하다. + +```ts +type ExampleType = { + readonly a: number; + readonly b: string; +}; + +type CreateMutable = { + -readonly [Property in keyof Type]: Type[Property]; +}; +type ResultType = CreateMutable; // { a: number; b: string } +``` + +### BottomSheet같은 예시로 알아보자. + +바텀 시트마다 각각의 resolver, isOpen 등의 상태를 관리하는 스토어가 필요한데 이 스토어의 타입을 선언해줘야 한다. + +BottomSheetMap에 존재하는 모든 키에 대해 일일이 스토어를 만들어 줄 수 있지만 불필요한 반복이 발생하게 된다. + +> 이럴 때는 인덱스 시그니처 문법을 사용해서 BottomSheetMap을 기반으로 각 키에 해당하는 스토어를 선언해 불필요한 반복을 줄여준다. + +```ts +const BottomSheetMap = { + RECENT_CONTACTS: RecentContactsBottomSheet, + CARD_SELECT: CardSelectBottomSheet, + SORT_FILTER: SortFilterBottomSheet, + PRODUCT_SELECT: ProductSelectBottomSheet, + REPLY_CARD_SELECT: ReplyCardSelectBottomSheet, + RESEND: ResendBottomSheet, + STICKER: StickerBottomSheet, + BASE: null, +}; + +export type BOTTOM_SHEET_ID = keyof typeof BottomSheetMap; + +// 불필요한 반복이 발생한다 +type BottomSheetStore = { + RECENT_CONTACTS: { + resolver?: (payload: any) => void; + args?: any; + isOpened: boolean; + }; + CARD_SELECT: { + resolver?: (payload: any) => void; + args?: any; + isOpened: boolean; + }; + SORT_FILTER: { + resolver?: (payload: any) => void; + args?: any; + isOpened: boolean; + }; + // ... +}; + +// Mapped Types를 통해 효율적으로 타입을 선언할 수 있다 +type BottomSheetStore = { + [index in BOTTOM_SHEET_ID]: { + resolver?: (payload: any) => void; + args?: any; + isOpened: boolean; + }; +}; +``` + +### 맵드 + as 키워드로 키를 재지정 가능하다! + +```ts +type BottomSheetStore = { + [index in BOTTOM_SHEET_ID as `${index}_BOTTOM_SHEET`]: { + resolver?: (payload: any) => void; + args?: any; + isOpened: boolean; + }; +}; +``` + +> 위의 예시처럼 키 이름을 그대로 쓰거나 모든 키에 특정 문자열을 붙이는 것처럼 새로운 키를 지정할 수 있다. + +
+
+ +## 6) 템플릿 리터럴 타입(Template Literal Types) + +자바스크립트의 템플릿 리터럴 문자열을 사용하여 문자열 리터럴 타입을 선언할 수 있는 문법이다. + +```ts +type Me = "chu" | "chuchu" | "seulgi" | "seulgi chu" | "chuseulgi"; + +type MyName = `${Me}-name`; +``` + +- Me 타입의 모든 유니온 멤버 뒤에 -name을 붙여 새로운 유니온 타입을 만들었다. +- 템플릿 리터럴을 사용하여 `${Me}-name`와 같이 변수 자리에 문자열 리터럴 유니온 타입인 Me를 넣으면 유니온 타입 멤버들이 차례로 해당 변수로 들어간다. +- 해당 변수로 들어가 -name이 붙은 문자열 리터럴의 유니온 타입을 결과로 반환한다. + +
+
+ +## 7) 제네릭(Generic) + +제네릭은 C, 자바 같은 정적 언어에서 다양한 타입 간에 재사용성을 높이기 위해 사용하는 문법이다. + +### 제네릭의 사전적 의미를 알아보자! + +제네릭의 사전적 의미는 **특징이 없거나 일반적인 것**인데 타입스크립트의 제네릭도 이와 비슷한 맥락으로 일반화된 데이터 타입이라고 할 수 있다. + +- 함수, 타입, 클래스 등에서 내부적으로 사용할 타입을 정해두지 않고 타입 변수를 사용한다. +- 변수로 해당 위치를 비워 둔 다음에, 실제로 그 값을 사용할 때 외부에서 타입 변수 자리에 타입을 지정해 사용하는 방식이다. + +> 이렇게 하면 함수, 타입, 클래스 등 여러 타입에 대해 하나하나 따로 정의하지 않아도 되기 때문에 재사용성이 크게 향상된다. + +### 제네릭 사용 방법을 알아보자! + +- ``와 같이 꺾쇠괄호 내부에 정의된다. +- 사용할 때는 함수에 매개변수를 넣는 것과 유사하게 원하는 타입을 넣어주면 된다. +- 타입 변수명으로 T(Type), E(Element), K(Key), V(Value) 등 한 글자로 된 이름을 많이 사용한다. + +```ts +type Example = T[]; + +const arr: Example = ["1", "2"]; +``` + +### any와는 어떤 점이 다를까? + +any 타입의 배열에서는 배열 요소들의 타입이 전부 같지 않을 수 있다. 쉽게 말해 타입 정보를 잃어버린 것과 같다. + +반면 제네릭은 any처럼 아무 타입을 무분별하게 받는 게 아니라 배열 생성 시점에 원하는 타입으로 특정할 수 있는 것이다. + +> 제네릭을 사용하면 배열 요소가 전부 동일한 타입으로 보장될 수 있다. + +### 다른 특징은 뭐가 있을까? + +- 제네릭 함수를 호출할 때 반드시 꺾쇠괄호(<>) 안에 타입을 명시해야 하는 것은 아니다. +- 타입을 명시하는 부분을 생략하면 컴파일러가 인수를 보고 타입을 추론한다. +- 타입 추론이 가능한 경우는 타입 명시를 생략할 수 있다. + +```ts +function example(arg: T): T[] { + return new Array(2).fill(arg); +} + +example("hi"); // T는 string으로 추론 +``` + +- 특정 요소 타입을 알 수 없을 때는 제네릭 타입에 기본값을 추가할 수 있다. + +```ts +interface SubmitEvent extends SyntheticEvent { + submitter: T; +} +``` + +### 특정 타입에서만 존재하는 멤버를 참조하려 하면 안된다! + +```ts +function example(arg: T): number { + return arg.length; // Error! +} +``` + +배열에만 존재하는 length 속성을 제네릭에서 참조하려고 하면 당연히 에러가 난다. + +컴파일러는 어떤 타입이 제네릭에 전달될지 알 수 없기 때문에 모든 타입이 length 속성을 사용할 수 없다 알려준다. + +```ts +interface Example { + length: number; +} + +function example(arg: T): number { + return arg.length; +} +``` + +> 이럴 때는 제네릭 꺾쇠괄호 내부에 "length 속성을 가진 타입만 받는다"라는 제약을 걸어준다. + +### 제네릭을 사용할 때 주의해야 할 점이 있다? + +파일 확장자가 tsx일 때 화살표 함수에 제네릭을 사용하면 에러가 발생한다. + +tsx는 타입스크립트 + JSX이므로 제네릭의 꺾쇠괄호와 태그의 꺾쇠괄호를 혼동하여 문제가 생긴다. + +### 이런 상황을 어떻게 피할 수 있을까? + +제네릭 부분에 extends 키워드를 사용해 컴파일러에게 특정 타입의 하위 타입만 올 수 있음을 확실히 알려주면 된다. + +```ts +// 에러 발생 : JSX element "T" has no corresponding closing tag. +const exampleFunc = (arg: T): T[] => { + return new Array(3).fill(arg); +}; + +// 에러 발생 X +const exampleFunc2 = (arg: T): T[] => { + return new Array(3).fill(arg); +}; +``` + +보통 제네릭을 사용할 때는 function 키워드로 선언하는 경우가 많다. From df4018f3517e6bcfffeff5fc542e7622fcc1a00d Mon Sep 17 00:00:00 2001 From: doitchuu Date: Fri, 21 Mar 2025 07:56:29 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[Docs]=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../seulgi.md" | 2 +- .../seulgi.md" | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" index 7769f6a..ee69d14 100644 --- "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" +++ "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.1_\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\247\214\354\235\230_\353\217\205\354\236\220\354\240\201_\355\203\200\354\236\205_\354\213\234\354\212\244\355\205\234/seulgi.md" @@ -198,7 +198,7 @@ console.log(Object.prototype.toString.call(arr)); // [object Array] ```ts const arr: number[] = [1, 2, 3]; // 숫자 원소만 허용 -const arr: Array = [1, 2, 3]; // 위와 도일한 타입 +const arr: Array = [1, 2, 3]; // 위와 동일한 타입 ``` - 자료형 + 대괄호([]) 형식으로 배열 타입 선언 diff --git "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.2_\355\203\200\354\236\205_\354\241\260\355\225\251/seulgi.md" "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.2_\355\203\200\354\236\205_\354\241\260\355\225\251/seulgi.md" index 6b4fab6..ec0273a 100644 --- "a/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.2_\355\203\200\354\236\205_\354\241\260\355\225\251/seulgi.md" +++ "b/CH03_\352\263\240\352\270\211_\355\203\200\354\236\205/3.2_\355\203\200\354\236\205_\354\241\260\355\225\251/seulgi.md" @@ -85,7 +85,7 @@ interface IndexSignature1 { interface IndexSignature2 { [key: string]: number; length: number; - isValid: boolean; + isValid: boolean; // Error! name: string; // Error! } ```