From 274a02b23c1c848be00e7ab4d5f8204fa15342c2 Mon Sep 17 00:00:00 2001 From: seoyeonjiin Date: Sun, 10 Aug 2025 01:13:35 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[7=EC=A3=BC=EC=B0=A8]=20item53=20-=20item57?= =?UTF-8?q?=20(=EC=A0=95=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yeonjin/item53.md" | 89 +++++++++++++++++++ .../yeonjin/item54.md" | 29 ++++++ .../yeonjin/item56.md" | 52 +++++++++++ .../yeonjin/item57.md" | 3 + 4 files changed, 173 insertions(+) create mode 100644 "7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item53.md" create mode 100644 "7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item54.md" create mode 100644 "7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item56.md" create mode 100644 "7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item57.md" diff --git "a/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item53.md" "b/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item53.md" new file mode 100644 index 0000000..19ae863 --- /dev/null +++ "b/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item53.md" @@ -0,0 +1,89 @@ +## [ITEM 53] 타입스크립트 기능보다는 ECMAScript 기능을 사용하기 + +자바스크립트에 새로 추가된 기능과, 초기 타입스크립트가 독립적으로 개발했던 기능의 호환성 문제로 피해야할 몇가지 기능들 + +### 열거형(enum) + +- 열거형은 상황에 따라 다르게 동작한다. + - 숫자 열거형에 0,1,2 외의 다른 숫자가 할당되면 위험하다. + - 상수 열거형은 보통의 열거형과 달리 런타임에서 완전히 제거된다. + - preserveConstEnums 플래그를 설정한 상태의 상수 열거형은 보통의 열거형처럼 상수 열거형 정보를 유지한다. + - 문자열 열거형은 명목적 타이핑을 사용한다. (타입의 이름이 같아야 할당 허용) +- 열거형 대신 리터럴 타입의 유니온을 사용한다. + + ```tsx + type Flavor = "vanilla" | "chocolate" | "strawberry"; + + let flavor: Flavor = "chocolate"; + flavor = "mint chip"; //~~~~ "mint chip" 유형은 'Flavor' 유형에 할당할 수 없습니다. + ``` + +### 매개변수 속성 + +- 아래 예시에서 public name은 매개변수 속성이다. + +```tsx +class Person { + constructor(public name: string) {} +} +``` + +- 일반 속성 + +```tsx +class Person { + name: string; + constructor(name: string) { + this.name = name; + } +} +``` + +- 일반 속성과 매개변수 속성 중 한 가지를 통일성 있게 사용하는 것이 좋다. + +### 네임스페이스와 트리플 슬래시 임포트 + +- 타입스트립트 자체 모듈 시스템 → module 키워드, 트리플 슬래시 임포트 +- ECMAScript 공식 모듈 시스템과 호환되기 위한 namespace 키워드 추가 (호환을 위함. 기본적으로는 import, export를 사용해야 함) + +```tsx +namespace foo { + function bar() {} +} + +/// +foo.bar(); +``` + +### 데코레이터 + +클래스, 메서드, 속성에 annotation을 붙이거나 기능을 추가하는 데 사용 + +```tsx +class Greeter { + greeting: string; + constructor(message: string) { + this.greeting = message; + } + @logged + greet() { + return "Hello, " + this.greeting; + } +} + +function logged(target: any, name: string, descriptor: PropertyDescriptor) { + const fn = target[name]; + descriptor.value = function () { + console.log(`Calling ${name}`); + return fn.apply(this, arguments); + }; +} + +console.log(new Greeter("Dave").greet()); +// 출력: +// Calling greet +// Hello, Dave +``` + +- tsconfig.json에 experimentalDecorators 속성 추가 +- 표준화 X → 데코레이터가 표준이 되기 전에는 타입스크립트에서 데코레이터를 사용하지 않는 게 좋다 diff --git "a/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item54.md" "b/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item54.md" new file mode 100644 index 0000000..284b0ec --- /dev/null +++ "b/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item54.md" @@ -0,0 +1,29 @@ +## [ITEM 54] 객체를 순회하는 노하우 + +### 타입 한정 + +- 키가 어떤 타입인지 정확히 파악하고 있다면 let k: keyof T와 for-in 루프를 사용한다. +- 함수의 매개변수로 쓰이는 객체에는 추가적인 키가 존재할 수 있다. + +```tsx +function foo(abc, ABC) { + let k: keyof ABC; // 이 코드를 추가하지 않으면 다르게 추론될 수 있음 + for (k in abc) { + // let k : "a" | "b" | "c" + const v = abc[k]; // string | number 타입 + } +} +``` + +→ 타입이 너무 좁아 문제가 발생할 수 있음 + +### Object.entries 사용하기 (일반적인 방법) + +```tsx +function foo(abc: ABC) { + for (const [k, v] of Object.entries(abc)) { + k; // string 타입 + v; // any 타입 + } +} +``` diff --git "a/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item56.md" "b/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item56.md" new file mode 100644 index 0000000..d36f354 --- /dev/null +++ "b/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item56.md" @@ -0,0 +1,52 @@ +## [ITEM 56] 정보를 감추는 목적으로 private 사용하지 않기 + +- 자바스크립트는 클래스에 비공개 속성을 만들 수 없다. +- 비공식적으로 앞에 \_언더스코어를 붙여서 비공개 속성을 표현했다. (실제론 접근할 수 있음) +- ts의 public, protected, priavte 키워드는 컴파일 이후에 제거된다. +- 정보를 감추기 위해 private을 사용하면 안 된다. + +### 클로저 사용하기 + +- 정보를 숨기기위한 방법 +- 외부에서 passwordHash 변수에는 접근할 수 없다. +- passwordHash에 접근해야 하는 메서드는 생성자 내부에 정의되어야 한다. +- 메서드 정의가 생성자 내부에 존재하면 인스턴스를 생성할 때마다 각 메서드의 복사본이 생성되기 때문에 메모리를 낭비하게 된다는 것을 기억해야 한다. +- 동일한 클래스로부터 생성된 인스턴스라고 하더라도 서로의 비공개 데이터에 접근하는 것이 불가능하다. (일반적인 객체지향 언어에서는 클래스 단위 비공개이지만, 클로저 방식은 인스턴스 단위 비공개) + +```tsx +declare function hash(text: string): number; + +class PasswordChecker { + checkPassword: (password: string) => boolean; + constructor(passwordHash: number) { + this.chekcPassword = (password: string) => { + return hash(password) === passwordHash; + }; + } +} + +const checker = new PasswordChecker(hash("s3cret")); +checker.checkPassword("s3cret"); // 결과는 true +``` + +### 비공개 필드 기능(#) 사용하기 + +- 접두사로 #을 붙여서 타입 체크와 런타임 모두에서 비공개로 만든다. +- 클래스 메서드나 동일한 클래스의 개발 인스턴스끼리는 접근이 가능하다. + +```tsx +class PasswordChecker { + #passwordHash: number; + constructor(passwordHash: number) { + this.#passwordHash = passwordHash; + { + + checkPassword(password: string) { + return hash(password) === this.#passwordHash; + } + } + + const checker = new PasswordChecker(hash('s3cret')); + checker.checkPassword('secret'); + checler.checkPassword('s3cret'); +``` diff --git "a/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item57.md" "b/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item57.md" new file mode 100644 index 0000000..9ebe3e5 --- /dev/null +++ "b/7\354\236\245-\354\275\224\353\223\234\353\245\274 \354\236\221\354\204\261\355\225\230\352\263\240 \354\213\244\355\226\211\355\225\230\352\270\260/yeonjin/item57.md" @@ -0,0 +1,3 @@ +## [ITEM 57] 소스맵을 사용하여 타입스크립트 디버깅하기 + +- 소스맵은 자바스크립트로 변환된 코드의 위치와 심벌들을 원본 코드의 원래 위치와 심벌들로 매핑한다. From 472e57d0040fd9563798f88d69beec5fd9cc3c8b Mon Sep 17 00:00:00 2001 From: seoyeonjiin Date: Sun, 10 Aug 2025 01:14:03 +0900 Subject: [PATCH 2/2] =?UTF-8?q?[7=EC=A3=BC=EC=B0=A8]=20item58=20-=20item62?= =?UTF-8?q?=20(=EC=A0=95=EB=A6=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yeonjin/item58.md" | 138 ++++++++++++++++++ .../yeonjin/item59.md" | 21 +++ .../yeonjin/item60.md" | 4 + .../yeonjin/item61.md" | 36 +++++ .../yeonjin/item62.md" | 4 + 5 files changed, 203 insertions(+) create mode 100644 "8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item58.md" create mode 100644 "8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item59.md" create mode 100644 "8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item60.md" create mode 100644 "8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item61.md" create mode 100644 "8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item62.md" diff --git "a/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item58.md" "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item58.md" new file mode 100644 index 0000000..3f05df6 --- /dev/null +++ "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item58.md" @@ -0,0 +1,138 @@ +## [ITEM 58] 모던자바스크립트로 작성하기 + +### ECMAScript 모듈 사용하기 + +```tsx +// a.ts +import * as b from "./b"; +console.log(b.name); + +// b.ts +export const name = "Module B"; +``` + +### 프로토타입 대신 클래스 사용하기 + +- 프로토타입으로 구현한 Person 객체보다 클래스로 구현한 Person 객체가 문법이 간결하고 직관적임. + +```tsx +class Person { + first: string; + last: string; + + constructor(first: string, last: string) { + this.first = first; + this.last = last; + } + + getName() { + return this.first + " " + this.last; + } +} + +const marie = new Person("Marie", "Curie"); +const personName = marie.getName(); +``` + +### var 대신 let/const 사용하기 + +### for(;;) 대신 for-of 또는 배열 메서드 사용하기 + +- 코드가 짧고 인덱스 변수를 사용하지 않기 때문에 실수를 줄일 수 있다. +- 인덱스가 필요한 경우는 forEach 메서드 사용 + +```tsx +for (const el of array) { + // ... +} + +array.forEach((el, i) => { + // ... +}); +``` + +### 함수 표현식보다 화살표 함수 사용하기 + +- 화살표 함수를 사용하면 상위 스코프의 this를 유지할 수 있다. +- 컴파일 옵션에 noImplicitThis(또는 strict)를 설정하면 타입스크립트가 this 바인딩 관련 오류를 표시해줌 + +```tsx +class Foo { + method() { + console.log(this); + [1, 2].forEach((i) => { + console.log(this); + }); + } +} + +const f = new Foo(); +f.method(); +// 항상 Foo, Foo, Foo을 출력합니다. +``` + +### 단축 객체 표현과 구조 분해 할당 사용하기 + +```tsx +const x = 1, + y = 2, + z = 3; +const pt = { x, y, z }; + +const { props } = obj; +const { a, b } = props; +``` + +### 함수 매개변수 기본값 사용하기 + +```tsx +function parseNum(str, base = 10) { + return parseInt(str, base); +} +``` + +### 저수준 프로미스나 콜백 대신 async/await 사용하기 + +- 코드가 간결해져 버그나 실수를 방지한다 +- 비동기 코드에 타입 정보가 전달되어 타입 추론을 가능하게 한다. + +```tsx +async function getJSON(url: string) { + const response = await fetch(url); + return response.json(); +} +``` + +### 연관 배열에 객체 대신 Map과 Set 사용하기 + +```tsx +function countWords(text:string) { + cosnt counts: {[word: string]: number) = {}; + for (const word of text.split(/[\s,.\+/)) { + counts[word] = 1 + (counts[word] || 0); + } + return counts; +} + +console.log(countWords('Objects have a constructor')); + +{ + Objects: 1, + have: 1, + a: 1, + constructor: "1function Object() { [native code] }" // 예상하지 못한 동작 +} + +// Map 사용하기 +function countWordsMap(text: string) { + const counts = new Map(); + for (const word of text.split(/[\s,.\+/)) { + count.set(wrod, 1 + (counts.get(word) }} 0)); + } + return counts; +} +``` + +### 타입스크립트에 use strict 넣지 않기 + +- 타입스크립트에서 수행되는 안전성 검사가 엄격 모드보다 훨씬 더 엄격한 체크를 하기 때문에, 타입스크립트 코드에서 ‘user strict’는 무의미하다. diff --git "a/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item59.md" "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item59.md" new file mode 100644 index 0000000..e59d99b --- /dev/null +++ "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item59.md" @@ -0,0 +1,21 @@ +## [ITEM 59] 타입스크립트 도입 전에 @ts-check 와 JSDoc으로 시험해보기 + +- ts-check 지시자는 noImplicitAny 설정을 해제한 것보다 헐거운 체크를 수행한다. + +### 선언되지 않은 전역 변수 + +- 변수를 제대로 인식할 수 있도록 별도로 타입 선언 파일을 만든다 + +### 알 수 없는 라이브러리 + +- 서드파티 라이브러리를 사용하는 경우, 서드파티 라이브러리의 타입 정보를 알아야 ㅎ란다. +- 서드파티 라이브러리의 타입 선언을 설치한다. + +### DOM 문제 + +- 타입 단언문을 사용한다 +- 타입 단언문을 사용하지 못하는 경우 JSDoc을 사용하여 타입 단언을 대체할 수 있다. + +### 부정확한 JSDoc + +- ts-check 지시자와 JSDoc 주석을 장기간 사용하는 것은 좋지 않다. diff --git "a/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item60.md" "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item60.md" new file mode 100644 index 0000000..f40d823 --- /dev/null +++ "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item60.md" @@ -0,0 +1,4 @@ +## [ITEM 60] allowJs로 타이스크립트와 자바스크립트 같이 사용하기 + +- allowJs 컴파일러 옵션을 통해, 마이그레이션 중에 자바스크립트와 타입스크립트 파일을 서로 임포트할 수 있게 한다. +- 대규모 마이그레이션 작업 전에 테스트와 빌드 체인에 타입스크립트를 적용해야 한다. diff --git "a/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item61.md" "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item61.md" new file mode 100644 index 0000000..eb41bae --- /dev/null +++ "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item61.md" @@ -0,0 +1,36 @@ +## [ITEM 61] 의존성 관계에 따라 모듈 단위로 전환하기 + +- 다른 모듈에 의존하지 않는 최하단 모듈부터 작업을 시작해서 의존성의 최상단에 있는 모듈을 마지막으로 완성해야 한다. +- 서드파티 라이브러리 타입 정보를 가장 먼저 해결해야한다. +- 마이그레이션 할 때는 타입 정보 추가만 하고, 리팩토링을 해서는 안 된다. (나중에 리팩) + +### 선언되지 않은 클래스 멤버 + +- 자바스크립트는 클래스 멤버 변수를 선언할 필요가 없지만, 타입스크립트에서는 명시적으로 선언해야 함 + +### 타입이 바뀌는 값 + +- 한번에 생성하기 곤란한 경우에는 타입 단언문을 사용할 수도 있음 + +```tsx +const state = {}; +state.name = 'New York'; // 오류 +state.capital = 'Albany'; //오류 + +// 한번에 생성 +const state = { + name: 'New York'; + capital: 'Albany'; +}; // 정상 + +//타입 단언문 사용 +interface State { + name: string; + capital: string; +} + +const state = {} as State; +state.name = 'New York'; +state.capital = 'Albany'; + +``` diff --git "a/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item62.md" "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item62.md" new file mode 100644 index 0000000..1664da0 --- /dev/null +++ "b/8\354\236\245-\355\203\200\354\236\205\354\212\244\355\201\254\353\246\275\355\212\270\353\241\234 \353\247\210\354\235\264\352\267\270\353\240\210\354\235\264\354\205\230\355\225\230\352\270\260/yeonjin/item62.md" @@ -0,0 +1,4 @@ +## [ITEM 62] 마이그레이션의 완성을 위해 noImplicitAny 설정하기 + +- noImplicitAny 설정이 없으면 타입 선언과 관려된 실제 오류가 드러나지 않는다. +- 처음에는 noImplicitAny를 로컬에만 설정하고 타입 오류를 점진적으로 수정한다.