From acedb9ba734fd1edadc2bbf0f39ec9dedf3a388f Mon Sep 17 00:00:00 2001 From: y0unj1NoH Date: Tue, 12 Nov 2024 17:14:18 +0900 Subject: [PATCH] add: ch10-12 study --- .../\353\205\270\354\234\244\354\247\200.md" | 1058 +++++++++++++++++ 1 file changed, 1058 insertions(+) create mode 100644 "10\354\240\234\353\204\244\353\246\255-12IDE\352\270\260\353\212\245\354\202\254\354\232\251/\353\205\270\354\234\244\354\247\200.md" diff --git "a/10\354\240\234\353\204\244\353\246\255-12IDE\352\270\260\353\212\245\354\202\254\354\232\251/\353\205\270\354\234\244\354\247\200.md" "b/10\354\240\234\353\204\244\353\246\255-12IDE\352\270\260\353\212\245\354\202\254\354\232\251/\353\205\270\354\234\244\354\247\200.md" new file mode 100644 index 0000000..ad9eedf --- /dev/null +++ "b/10\354\240\234\353\204\244\353\246\255-12IDE\352\270\260\353\212\245\354\202\254\354\232\251/\353\205\270\354\234\244\354\247\200.md" @@ -0,0 +1,1058 @@ +


+ +

10. 제네릭

+ +
+ +- ts는 `제네릭`을 사용해 타입 간의 관계를 알아냄 + +# 10.1 제네릭 함수 + +- 함수의 매개변수나 반환값의 타입을 호출 시점에 결정할 수 있게 해주는 함수 +- 타입 안전성을 유지하면서 다양한 타입 처리 가능 +- ``와 같은 제네릭 타입 매개변수를 선언 (T는 함수 내부에서 사용할 타입) + +```ts +function identity(input: T): T { + return input; +} + +const numeric = identity("me"); // 타입: "me" +const stringy = identity(123); // 타입: 123 + +// 화살표 함수 예시 +const identity = (input: T): T => input; + +identity(123); // 타입: 123 +``` + + - 제네릭 화살표 함수 구문은 .tsx 파일에서 JSX 구문과 충동하므로 일부 제한 있음 + +## 10.1.1 명시적 제네릭 호출 타입 + +- ts 대부분의 경우 함수가 호출되는 방식을 보고 타입을 유추하지만, 때로는 ts가 타입을 유추할 수 없는 경우에는 타입을 지정해줘야 함 + ```ts + logWrapper((input) => { + console.log(input.length); // 타입: (input: string) => void + }); + ``` +- 제네릭 구조체에 두 개보다 많은 매개변수를 사용하지 않는 것을 권장 + +## 10.1.2 다중 함수 타입 매개변수 + +```ts +function makeTuple(first: First, second: Second) { + return [first, second] as const; +} + +let tuple = makeTuple(true, "abc"); // value: readonly [boolean, string] 타입 +``` + +- 여러 개의 타입 매개변수를 선언하면, 해당 함수에 대한 호출은 명시적으로 모든 제네릭 타입을 선언하지 않거나 모두 선언해야 함 + + ```ts + function makePair(key: Key, value: Value) { + return { key, value }; + } + + // Ok: 타입 인수가 둘 다 제공되지 않음 + makePair("abc", 123); // 타입: { key: string; value: number } + + // Ok: 두 개의 타입 인수가 제공됨 + makePair("abc", 123); // 타입: { key: string; value: number } + makePair<"abc", 123>("abc", 123); // 타입: { key: "abc"; value: 123 } + + // Error: Expected 2 type arguments, but got 1. + makePair("abc", 123); + ``` + +# 10.2 제네릭 인터페이스 + +```ts +let stringyBox: Box = { + inside: "abc" +}; + +let numberBox: Box = { + inside: 123 +}; + +let incorrectBox: Box = { + inside: false + // Error: Type 'boolean' is not assignable to type 'number'. +}; +``` + +- 내장 Array 인터페이스 + + ```ts + interface Array { + /** + * 배열에서 마지막 요소를 제거하고 그 요소를 반환합니다. + * 배열이 비어 있는 경우 undefined를 반환하고 배열은 수정되지 않습니다. + */ + pop(): T | undefined; + + /** + * 배열의 끝에 새로운 요소를 추가하고 배열의 길이를 반환합니다. + * @param items 배열에 추가된 새로운 요소 + */ + push(...items: T[]): number; + } + ``` + +# 10.2.1 유추된 제네릭 인터페이스 타입 + +```ts +interface LinkedNode { + next?: LinkedNode; + value: Value; +} + +function getlast(node: LinkedNode): Value { + return node.next ? getlast(node.next) : node.value; +} + +let lastDate = getlast({ + value: new Date("09-13-1993") +}); + +let lastFruit = getlast({ + next: { + value: "banana" + }, + value: "apple" +}); + +let lastMismatch = getlast({ + next: { + value: 123 + }, + value: false +}); +``` + +- 인터페이스가 제네릭 타입을 선언하면, 타입 인수를 제공해야 함 + +```ts +interface Cratelike { + contents: T; +} + +let missingGeneric: Cratelike = { + contents: "??" +}; +// Error: Generic type 'Crate' requires 1 type argument(s). +``` + +# 10.3 제네릭 클래스 + +- 인터페이스와 마찬가지로 클래스도 타입 매개 변수를 선언하여 나중에 사용할 수 있음 + +```ts +class Secret { + key: Key; + value: Value; + + constructor(key: Key, value: Value) { + this.key = key; + this.value = value; + } + + getValue(key: Key): Value | undefined { + return this.key === key ? this.value : undefined; + } +} + +const storage = new Secret(12345, "luggage"); // 타입: Secret +const result = storage.getValue(1987); // 타입: string | undefined +``` + +## 10.3.1 명시적 제네릭 클래스 타입 + +- 셍성자에 전달된 매개변수의 타입으로부터 타입 인수를 유추할 수 있음 +- 유추할 수 없는 경우, 타입 인수의 기본값은 `unknown` + +```ts +class CurriedCallback { + #callback: (input: Input) => void; + + constructor(callback: (input: Input) => void) { + this.#callback = (input: Input) => { + console.log("Input: ", input); + callback(input); + }; + } + + call(input: Input) { + this.#callback(input); + } +} + +// 타입: CurriedCallback +new CurriedCallback((input: string) => { + console.log(input.length); +}); + +// 타입: CurriedCallback +new CurriedCallback((input) => { + console.log(input.length); + // Error: Property 'length' does not exist on type 'unknown'. +}); +``` + +## 10.3.2 제네릭 클래스 확장 + +- `extends` 키워드로 확장 가능 + +```ts +class Quote { + lines: T; + + constructor(lines: T) { + this.lines = lines; + } +} + +class SpokenQuote extends Quote { + speak() { + console.log(this.lines.join("\n")); + } +} + +const spoken = new SpokenQuote([ + "Greed is so destructive.", + "It destroys everything" +]); +spoken.speak(); // 출력: Greed is so destructive.\nIt destroys everything +``` + +- 제네릭 파생 클래스는 자체 타입 인수를 기본 클래스에 전달 가능 (타입 이름은 일치하지 않아도 됨) + +```ts +class AttributedQuote extends Quote { + speaker: string; + + constructor(value: Value, speaker: string) { + super(value); + this.speaker = speaker; + } +} + +// 타입: AttributedQuote +const attributed = new AttributedQuote( + "The road to success is always under construction.", + "Lily Tomlin" +); +console.log(attributed.lines); // 출력: The road to success is always under construction. +console.log(attributed.speaker); // 출력: Lily Tomlin +``` + +## 10.3.3 제네릭 인터페이스 구현 + +- 제네릭 인터페이스를 구현할 때도 마찬가지로 기본 인터페이스의 모든 타입 매개변수는 클래스에 선언되어야 함 + +```ts +interface ActingCredit { + role: Role; +} + +class MoviePart implements ActingCredit { + role: string; + speaking: boolean; + + constructor(role: string, speaking: boolean) { + this.role = role; + this.speaking = speaking; + } +} + +const part = new MoviePart("Miranda Priestly", true); +console.log(part.role); // 출력: Miranda Priestly + +// 타입 오류 +class IncorrectExtension implements ActingCredit { + role: boolean; + // Error: Property 'role' in type 'IncorrectExtension' is not + // assignable to the same property in base type 'ActingCredit'. + // Type 'boolean' is not assignable to type 'string'. +} +``` + +## 10.3.4 메서드 제네릭 + +- 클래스 메서드는 클래스 인스턴스와 별개로 자체 제네릭 타입 선언 가능 + +```ts +class CreatePairFactory { + key: Key; + + constructor(key: Key) { + this.key = key; + } + + createPair(value: Value) { + return { key: this.key, value }; + } +} + +// 타입: CreatePairFactory +const factory = new CreatePairFactory("role"); + +// 타입: { key: string, value: number } +const numberPair = factory.createPair(10); +console.log(numberPair); // 출력: { key: 'role', value: 10 } + +// 타입: { key: string, value: string } +const stringPair = factory.createPair("Sophie"); +console.log(stringPair); // 출력: { key: 'role', value: 'Sophie' } +``` + +## 10.3.4 정적 클래스 제네릭 + +- `static` 멤버는 인스턴스 멤버와 구별되며, 특정 인스턴스와 연결되지 않음 +- 정적 클래스 메서드는 자체 타입 매개변수를 선언할 수 있지만, 클래스에 선언된 어떤 타입 매개변수에도 접근 불가능 + +```ts +class BothLogger { + instancelog(value: OnInstance) { + console.log(value); + return value; + } + + static staticlog(value: OnStatic) { + // let fromInstance: OnInstance; // Error: Static members cannot reference class type arguments. + console.log(value); + return value; + } +} + +// 타입: BothLogger +const logger = new BothLogger(); +logger.instancelog([1, 2, 3]); // 출력: [1, 2, 3] + +// 유추된 OnStatic 타입 인수: boolean[] +BothLogger.staticlog([false, true]); // 출력: [false, true] + +// 유추된 OnStatic 타입 인수: string +BothLogger.staticlog("You can't change the music of your soul."); // 출력: You can't change the music of your soul. +``` + +# 10.4 제네릭 타입 별칭 + +```ts +type Nullish = T | null | undefined; + +type CreatesValue = (input: Input) => Output; + +// 타입: (input: string) => number +let creator: CreatesValue; + +// 올바른 할당 +creator = (text) => text.length; // Ok + +// 타입 오류: 반환 타입이 맞지 않음 +creator = (text) => text.toUpperCase(); +// Error: Type 'string' is not assignable to type 'number'. +``` + +## 10.4.1 제네릭 판별된 유니언 + +- 타입 내로잉을 통해 특정 타입을 좁히는 데 사용되는 패턴 +- 성공적인 결과와 오류를 나타내는 제네릭 **결과** 타입을 만들기 위해 자주 사용됨 + +```ts +type Result = FailureResult | SuccessfulResult; + +interface FailureResult { + error: Error; + succeeded: false; +} + +interface SuccessfulResult { + data: Data; + succeeded: true; +} + +function handleResult(result: Result) { + if (result.succeeded) { + // result: SuccessfulResult 타입 + console.log(`We did it! ${result.data}`); + } else { + // result: FailureResult 타입 + console.error(`Awww... ${result.error}`); + } +} + +// 사용 예제 +const successResult: Result = { data: "Success!", succeeded: true }; +const failureResult: Result = { + error: new Error("Failure"), + succeeded: false +}; + +handleResult(successResult); // 출력: We did it! Success! +handleResult(failureResult); // 출력: Awww... Error: Failure +``` + +# 10.5 제네릭 제한자 + +## 10.5.1 제네릭 기본값 + +- 타입 인수가 명시적으로 제공되지 않거나, 유추할 수 없는 경우에 사용 + +```ts +interface Quote { + value: T; +} + +// 명시적으로 number 타입 지정 +let explicit: Quote = { value: 123 }; + +// 기본값 string 사용 +let implicit: Quote = { + value: "Be yourself. The world worships the original." +}; + +// 타입 오류: number는 string에 할당할 수 없음 +let mismatch: Quote = { value: 123 }; +// Error: Type 'number' is not assignable to type 'string'. +``` + +- 기본값이 있는 제네릭 타입 매개변수는 선언 목록의 마지막에 위치해야 함 + +```ts +function inTheEnd() {} // Ok +function inTheMiddle() {} +// Error: Required type parameters may not follow optional type parameters. +``` + +# 10.6 제한된 제네릭 타입 + +- `extends` 키워드로 제한할 타입을 명시 + +```ts +interface WithLength { + length: number; +} + +function logWithLength(input: T) { + console.log(`Length: ${input.length}`); + return input; +} + +logWithLength("No one can figure out your worth but you."); // 타입: string +logWithLength([false, true]); // 타입: boolean[] +logWithLength({ length: 123 }); // 타입: { length: number } +logWithLength(new Date()); // 타입 오류: Date는 length 속성이 없음 +// Error: Argument of type 'Date' is not assignable to parameter of type 'WithLength'. +// Property 'length' is missing in type 'Date' but required in type 'WithLength'. +``` + +## 10.6.1. keyof와 제한된 타입 매개변수 + +- `extends`와 `keyof`를 함께 사용하면 타입 매개변수를 이전 타입 매개변수의 키로 제한 가능 +- 제네릭 타입의 키를 지정하는 유일한 방법 + +```ts +function get(container: T, key: Key) { + return container[key]; +} + +const roles = { + favorite: "Fargo", + others: ["Almost Famous", "Burn After Reading", "Nomadland"] +}; + +const favorite = get(roles, "favorite"); // 타입: string +const others = get(roles, "others"); // 타입: string[] +const missing = get(roles, "extras"); // 타입 오류: 'extras'는 'roles'의 키가 아님 +// Error: Argument of type '"extras"' is not assignable to parameter of type '"favorite" | "others"'. +``` + +- `keyof` 없이 사용하면 제네릭 `key` 매개변수를 올바르게 입력할 방법이 없음 + + ```ts + function get(container: T, key: keyof T) { + return container[key]; + } + + const roles = { + favorite: "Fargo", + others: ["Almost Famous", "Burn After Reading", "Nomadland"] + }; + + // 모든 속성 값에 대한 유니언 타입이 됨 + const found = get(roles, "favorite"); // 타입: string | string[] + ``` + +# 10.7 Promise + +- `Promise`는 네트워크 요청과 같은 비동기 작업을 처리하기 위해 사용됨 +- 성공적으로 완료되면 `resolve`를, 오류가 발생하면 `reject`를 호출함 + +- `Promise`는 제네릭 타입을 사용하여 최종적으로 `resolve`된 값의 타입을 나타냄 + +## 10.7.1 Promise 생성 + +- `Promise` 생성자는 단일 매개변수를 받고, 이 매개변수 타입은 제네릭 `Promise` 클래스에 선언된 타입 매개변수에 의존함 + + ```ts + class Promiselike { + constructor( + executor: ( + resolve: (value: Value) => void, + reject: (reason: unknown) => void + ) => void + ) { + // ... + } + } + + // 타입: Promise + const resolvesUnknown = new Promise((resolve) => { + setTimeout(() => resolve("Done!"), 1000); + }); + + // 타입: Promise + const resolvesString = new Promise((resolve) => { + setTimeout(() => resolve("Done!"), 1000); + }); + ``` + + - `Promise`의 제네릭 `then` 메서드는 반환되는 `Promise`의 `resolve`된 값을 나타내는 새로운 타입 매개변수를 받음 + + ```ts + // 타입: Promise + const textEventually = new Promise((resolve) => { + setTimeout(() => resolve("Done!"), 1000); + }); + + // 타입: Promise + const lengthEventually = textEventually.then((text) => text.length); + ``` + +## 10.7.2 Async 함수 + +- `async` 키워드를 사용해 선언한 함수는 항상 `Promise`를 반환함 (반환타입은 항상 `Promise` 형태여야 함) +- `async` 함수는 반환된 값이 `Thenable`인 경우, `Promise.resolve`를 래핑함 + +```ts +// 타입: (text: string) => Promise +async function lengthAfterSecond(text: string): Promise { + await new Promise((resolve) => setTimeout(resolve, 1000)); + return text.length; +} + +// 타입: (text: string) => Promise +async function lengthImmediately(text: string): Promise { + return text.length; +} + +// Ok => Promise.resolve("Done!")으로 래핑 +async function givesPromiseForString(): Promise { + return "Done!"; +} + +// 타입 오류: 반환 타입이 Promise가 아님 +async function givesString(): string { + // Error: The return type of an async function or method must be the global Promise type. + return "Done!"; +} +``` + +# 10.8 제네릭 올바르게 사용하기 + +- 제네릭 무지성 사용 금지 (코드가 복잡해질 가능성 큼) +- 유틸리티 라이브러리에 대한 타입, 특히 범용 모듈은 경우에 따라 제네릭을 많이 사용할 수도 있음 + +## 10.8.1 제네릭 황금률 + +- 타입 매개변수가 최소 두 번 이상 사용되면 제네릭이 필요하다는 것 +- 각 함수 타입 매개변수는 매개변수에서 사용되어야 하고, 그다음 적어도 하나의 다른 매개변수 또는 함수의 반환 타입에서도 사용되어야 함 + +- 잘못된 제네릭 사용 예제 + + ```ts + function logInput(input: Input) { + console.log("Hi!", input); + } + ``` + + - Input 타입 매개변수가 선언하기 위해서만 사용되므로, 제네릭 X + + ```ts + function logInput(input: string) { + console.log("Hi!", input); + } + ``` + +- 유용한 제네릭 사용 예제 + + ```ts + function identity(arg: T): T { + return arg; + } + ``` + + - T가 선언, 반환 타입 모두 사용되므로, 제네릭 Good + +## 10.8.1 제네릭 명명 규칙 + +- 첫 번째 타입 인수는 T를, 그다음은 U, V 등이 사용됨 + + ```ts + function map(array: T[], transform: (item: T) => U): U[] { + return array.map(transform); + } + ``` + +- 타입 인수가 어떻게 사용되어야 하는지 맥락과 관련된 정보가 알려진 경우, 명명 규칙은 해당 용어의 첫 글자를 사용함 + + ```ts + interface State { + state: S; + } + + interface KeyValuePair { + key: K; + value: V; + } + ``` + +- 제네릭의 의도가 단일 문자에서 명확하지 않을 경우, 타입이 사용되는 용도를 가리키는 이름을 사용 + + ```ts + function labelBox(label: Label, value: Value) { + // ... + } + ``` + +


+ +

11. 선언 파일

+ +
+ +- ts로 작성된 패키지조차도 js 파일로 배포됨 + +- ts 프로젝트는 전역 변수와 APi 같은 환경에 특화된 기능의 타입 형태를 알려주는 방법이 필요함 (ex: Node.js에서 실행되는 프로젝트는 브라우저에서 사용할 수 없는 내장 Node.js 모듈에 접근할 수 있으며 그 반대로 가능) + +- ts는 구현과 별도로 타입 형태를 선언할 수 있음 +- `.d.ts` 확장자로 끝나는 선언 파일에 작성됨 +- 선언 파일은 프로젝트 내에서 작성되고, 컴파일된 npm 패키지로 배포되거나 독립 실행형 typings 패키지로 공유될 수 있음 + +# 11.1 선언 파일 + +- `.d.ts` 파일에는 사용 가능한 런타임 값, 인터페이스, 모듈, 일반적인 타입의 설명만 포함됨 +- js로 컴파일할 수 있는 모든 런타임 코드는 포함 불가능 + +```ts +// types.d.ts +export interface Character { + catchphrase? : string; + name: string; +} + +// index .ts +import { Character } from ".ltypes"; +export canst character: Character = { + catchphrase : "Yee-haw! ", + name : 11Sandy Cheeks 11, +}; +``` + +- **선언 파일은 값이 아닌 타입만 선언할 수 있는 코드 영역(앰비언트 컨텍스트)을 생성** + +# 11.2 런타임 값 선언 + +- 선언 파일(`.d.ts`)은 함수나 변수 같은 런타임 값을 생성하지 않지만, `declare` 키워드를 사용해 이런 구조체가 존재한다고 선언 가능 +- 외부 작업(ex: 웹 페이지의 ; + +// types/window.d.ts +interface Window { + myVersion: string; +} + +// index.ts +export function logWindowVersion() { + console.log(`Window version is: ${window.myVersion}`); +} +window.alert("Built-in window types still work! Hooray!"); +``` + + - `window` 객체에 `myVersion` 속성을 추가하고, 이를 전역 인터페이스에 병합 + +## 11.2.3 전역 확장 + +- 전역 범위로 확장이 필요한 경우, `declare global` 코드 블록을 사용하여 해당 블록 내용이 전역 컨텍스트에 있다고 표시 + +```ts +// types/data.d.ts +export interface Data { + version: string; +} + +// types/globals.d.ts +import { Data } from "./data"; + +declare global { + const globallyDeclared: Data; +} + +declare const locallyDeclared: Data; + +// index.ts +import { Data } from "./types/data"; + +function logData(data: Data) { + console.log(`Data version is: ${data.version}`); +} + +logData(globallyDeclared); // Ok +logData(locallyDeclared); // Error: Cannot find name 'locallyDeclared'. +``` + +# 11.3 내장된 선언 + +- 타입 검사를 강화하기 위해 내장된 선언을 사용 +- 주요 전역 객체 + - Array: 배열 객체 + - Function: 함수 객체 + - Map: 키-값 쌍을 저장하는 객체 + - Set: 고유한 값들의 집합을 저장하는 객체 + +## 11.3.1 라이브러리 선언 + +- ts는 js에 내장된 전역 객체에 대한 타입 선언을 제공 (`lib.[target].d.ts` 파일에 포함) + - `target`: 프로젝트에서 지원하는 자바스크립트의 최소 버전(예: ES5, ES2020, ESNext) + - `node_modules/typescript/lib/lib.es5.d.ts`와 같은 경로에서 확인 가능 + - IDE 기능인 **Go to Definition**를 통해서도 라이브러리 파일 확인 가능 + +```ts +// lib.es5.d.ts +interface Array { + /** + * 배열의 길이를 가져오거나 설정합니다. + * 배열의 가장 큰 인덱스보다 항상 더 큰 숫자입니다. + */ + length: number; + // ... +} +``` + +- Target + + - `tsconfig.json` 파일의 `target` 설정에 따라 적절한 lib 파일을 포함 + - `target`이 `es2016`인 프로젝트는 `lib.es5.d.ts`, `lib.es2015.d.ts`, `lib.es2016.d.ts` 파일을 포함 + - ex: ES2015에 추가된 EPSILON, isFinite와 같은 정적 Number 멤버는 lib . es2015 . + d.t 에 나열됨 + + ```ts + // lib.es2015.d.ts + interface NumberConstructor { + /** + * Number.EPSILON의 값은 2.2204460492503130808472633361816 x 10^-16입니다. + */ + readonly EPSILON: number; + + /** + * 전달된 값이 유한한 경우 true를 반환합니다. + * 전역 isFinite와 달리, Number.isFinite는 강제로 매개변수를 숫자로 변환하지 않습니다. + */ + isFinite(number: unknown): boolean; + } + ``` + +## 11.3.2 DOM 선언 + +- 웹 브라우저를 위한 DOM 타입 선언도 제공 (`lib.dom.d.ts` 파일에 포함) + +```ts +// lib.dom.d.ts +interface Storage { + /** + * 키/값 쌍의 수를 반환합니다. + */ + readonly length: number; + + /** + * 모든 키/값 쌍을 제거합니다. + */ + clear(): void; + + /** + * 주어진 키에 연결된 현잿값을 반환하거나, 주어진 키가 존재하지 않는 경우 null을 반환합니다. + */ + getItem(key: string): string | null; + // ... +} +``` + + - `Storage` 인터페이스는 `localStorage`와 `sessionStorage`에 사용됨 + +# 11.4 모듈 선언 + +- `declare` 키워드를 사용하여 모듈의 상태를 타입 시스템에 알릴 수 있음 + +```ts +// modules.d.ts +declare module "my-example-lib" { + export const value: string; +} + +// index.ts +import { value } from "my-example-lib"; +console.log(value); // Ok +``` + +## 11.4.1 와일드카드 모듈 선언 + +- 와일드카드(`*`)를 사용하여 특정 패턴과 일치하는 모든 모듈을 선언할 수 있음 +- 와일드카드 모듈 선언은 타입 정합성을 완벽히 보장하지 않으므로 주의가 필요함 + +```ts +// styles.d.ts +declare module "*.module.css" { + const styles: { [i: string]: string }; + export default styles; +} + +// component.ts +import styles from "./styles.module.css"; +console.log(styles.anyClassName); // 타입: string +``` + + - `styles.d.ts` 파일에서 `"*.module.css"` 패턴과 일치하는 모든 모듈을 선언 + - 모듈은 `{ [i: string]: string }` 타입의 객체를 내보냅니다. + +# 11.5 패키지 타입 + +- ts로 작성된 프로젝트는 여전히 `.js`로 컴파일된 파일을 포함한 패키지를 배포함 +- 일반적으로 `.d.ts` 파일을 사용하여 이러한 자바스크립트 파일 뒤에 타입스크립트 타입 시스템 형태를 지원 + +## 11.5.1 선언 + +- ts는 입력된 파일에 대한 .d.ts 출력 파일과 자바스크립트 출력 파일을 함께 생성하는 declaration 옵션을 제공 + +```ts +// index.ts +export const greet = (text: string) => { + console.log(`Hello, ${text}!`); +}; +``` + +```ts +// 컴파일 결과 +// index.d.ts +export declare const greet: (text: string) => void; + +// index.js +export const greet = (text) => { + console.log(`Hello, ${text}!`); +}; +``` + +## 11.5.2 패키지 타입 의존성 + +- ts는 프로젝트의 `node_modules` 의존성 내부에서 번들로 제공되는 `.d.ts` 파일을 감지하고 활용할 수 있음 +- 이러한 파일은 해당 패키지에서 내보낸 타입 형태에 대해 타입 시스템에 알림 + +``` +lib/ + index.js + index.d.ts +package.json +``` + +- Jest 패키지 타입 의존성 + + - Jest는 `describe`와 `it` 같은 함수를 제공하는 `@jest/globals` 패키지에 대한 의존성을 가짐 + + ```ts + // package.json + { + "devDependencies": { + "jest": "^27.1.0" + } + } + + // using-globals.d.ts + describe("MyAPI", () => { + it("works", () => { /* ... */ }); + }); + + // using-imported.d.ts + import { describe, it } from "@jest/globals"; + + describe("MyAPI", () => { + it("works", () => { /* ... */ }); + }); + ``` + +## 11.5.3 패키지 타입 노출 + +- 프로젝트가 npm에 배포되고 사용자에게 타입을 제공하려면, 패키지의 `package.json` 파일에 `types` 필드를 추가하여 루트 선언 파일을 가리킴 +- `types` 필드는 `main` 필드와 유사하게 작동하며, `.js` 확장자 대신 `.d.ts` 확장자를 사용 + +```json +{ + "author": "Pendant Publishing", + "main": "./lib/index.js", // 런타임 파일 + "name": "coffeetable", + "types": "./lib/index.d.ts", // 타입 선언 파일 + "version": "0.5.22" +} +``` + +- ts는 `types` 필드에 지정된 파일을 사용하여 패키지에서 제공하는 타입을 인식 +- 만약 types 필드가 존재하지 않으면, 타입스크립트는 기본적으로 `./index.d.ts` 파일을 가정함 (이는 npm이 types 필드가 지정되지 않은 경우 `./index.js` 파일을 기본 진입점으로 가정하는 동작을 반영한 것) + +# 11.6 DefinitelyTyped + +- 모든 프로젝트가 ts로 작성된 것이 아니기에, ts 프로젝트는 js로 작성된 패키지의 타입을 알아야 함 +- DefinitelyTyped(DT): 커뮤니티에서 작성된 패키지 정의를 수용하는 거대한 저장소 +- DT 패키지는 npm에 `@types/이름`으로 게시됨 + +```ts +// package.json +{ + "dependencies": { + "lodash": "^4.17.21" + }, + "devDependencies": { + "@types/lodash": "^4.14.182" + } +} +``` + +## 11.6.1 타입 사용 가능성 + +- 아직 사용 가능한 타입이 없는 패키지에서 타입을 얻는 세 가지 방법 1.** DefinitelyTyped에 Pull Request 보내기** 2. **프로젝트 내에서 타입 작성**: declare module 구문을 사용해 프로젝트 내에서 타입 작성 3. **noImplicitAny 옵션 비활성화** + +


+ +

12. IDE 기능 사용

+ +
+ +# 12.1 코드 탐색 + +## 12.1.1 정의 찾기 + +- [Go to Definition] (`F12`): 요청된 이름이 원래 정의된 위치로 즉시 이동 +- `Ctrl` + 이름: 정의된 곳으로 이동 +- [Peek Definition] (`Alt` + `F12`): 정의를 보여주는 Peek 상자 +- [Go to Type Definition]: 클래스 또는 인터페이스의 인스턴스에 이 기능을 실행하는 경우에 인스턴스가 정의된 위치를 보여주는 대신 클래스 or 인터페이스 자체를 표시 + +## 12.1.2 참조 찾기 + +- [Go to Reference] (`Shift` + `F12` -> 마우스 오른쪽 클릭): Peek 상자를 통해 해당 타입 정의 또는 값의 참조 목록을 보여줌 +- [Find All References] (`Alt` + `Shift` + `F12`): 코드 탐색 후에도 사이드바 뷰에서 확인 가능하기 때문에, 한 번에 둘 이상의 참조를 열거나 수행하는 데 유용 + +## 12.1.3 구현 찾기 + +- [Go to Ilnplen1entations], [Find All Imple1nentations] : 코드에서 인터페이스 또는 추상 메서드의 모든 구현을 찾음 +- 클래스 또는 인터페이스와 같은 타입으로 입력된 값이 어떻게 사용되는지 구체적으로 검색할 때 특히 유용함 + +# 12.2 코드 작성 + +## 12.2.1 이름 완성하기 + +## 12.2.2 자동 가져오기 업데이트 + +- 파일명 바꾸거나, 다른 폴더로 파일을 이동하는 경우 import문 자동으로 업데이트 + +## 12.2.3 코드 액션 + +- 하나 이상의 코드 액션을 사용할 수 있는 경우 텍스트 커서 위에 클릭 가능한 전구 아이콘을 클릭 혹은 `Ctrl` + `.`로 사용 + +- [Rename Symbol] (`F2`): 해당 함수와 해당 함수를 호출하는 모든 곳의 이름 변경 일괄 적용 +- `Shift` + `Enter`: 새 이름을 적용하기 전에 발생할 모든 텍스트 변경사항을 나열하는 리팩터링 미리보기 창을 열 수 있음 + +- **사용하지 않는 코드 제거** + +# 12.3 오류를 효과적으로 처리하기 + +## 12.3.1 언어 서비스 오류 + +- PROBLEMS 탭 + + - [View Problem] (`F8` or `F8` + `Shift`): 오류를 이동하는 단축키 + - [PROBLEMS] 탭에서 오류를 클릭하면 텍스트 커서가 문제가 되는 행과 열로 이동 + - 현재 열려 있는 파일에 대한 오류만 표시되므로 주의! + +- 터미널 컴파일러 실행 + + - ts 컴파일러의 watch 모드: 파일에 있는 오류뿐만 아니라 모든 오류에 대한 실시간 업데이트 목록이 제공됨 + - `tsc -b` or `tsc -b -w` (프로젝트 참조 사용 시) + - `Ctrl`에서 파일 이름을 클릭하면, 텍스트 커서가 해당 파일의 잘못된 행과 열로 이동 + +- 타입 이해 + - `Ctrl`에서 변수를 호버하면, 이름이 선언된 위치를 표시 + +``` + +```