An open API service indexing awesome lists of open source software.

https://github.com/ssi02014/typescript-todolist

πŸ“š TypeScript κ³΅λΆ€ν•˜λ©΄μ„œ Classν˜•νƒœλ‘œ λ§Œλ“€μ–΄λ³΄λŠ” ToDoList
https://github.com/ssi02014/typescript-todolist

Last synced: 8 months ago
JSON representation

πŸ“š TypeScript κ³΅λΆ€ν•˜λ©΄μ„œ Classν˜•νƒœλ‘œ λ§Œλ“€μ–΄λ³΄λŠ” ToDoList

Awesome Lists containing this project

README

          

# πŸ’» TypeScript-ToDoList
νƒ€μž…μŠ€ν¬λ¦½νŠΈλ‘œ λ§Œλ“€μ–΄λ³΄λŠ” ToDoList πŸ“‘


## πŸ“ˆ ν”„λ‘œμ νŠΈ μ‹œμž‘ μ „ μ…‹νŒ… κ³Όμ •
### πŸ” 1. νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ„€μΉ˜
```
node install -g typescript
npm install -g ts-node
```


### πŸ” 2. tsconfig.json 생성 및 μ„€μ •
```
//tsconfig.json 생성
tsc --init
```


- tsconfig.json μ„€μ •
```json
{
"compilerOptions": {
/* Basic Options */
"target": "es5",
"module": "commonjs",
"outDir": "./build", //λΉŒλ“œ 된 이후 μžλ°”μŠ€ν¬λ¦½νŠΈ 파일이 μ €μž₯ 될 μž₯μ†Œ
"rootDir": "./src", //νƒ€μž…μŠ€ν¬λ¦½νŠΈ μ†ŒμŠ€μ½”λ“œκ°€ μžˆλŠ” μ €μž₯μ†Œ

/* Strict Type-Checking Options */
"strict": true,

/* Module Resolution Options */
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}

```


### πŸ” 3. package.json 생성 및 μ„€μ •
```
//package.json 생성
npm init -y
```


- package.json μ„€μ •
```json
{
...
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc -w",
"start:run": "nodemon build/index.js",
"start": "concurrently npm:start:*"
},
...
}
```

### πŸ” nodemon, concurrently μ„€μΉ˜
```
npm install nodemon concurrently
```
- concurrentlyλŠ” λ™μ‹œμ— μ—¬λŸ¬ λͺ…λ Ήμ–΄λ₯Ό μ‹€ν–‰ν•˜κΈ° μœ„ν•΄ μ‚¬μš©λœλ‹€.
- nodemon: μ‹€μ‹œκ°„μœΌλ‘œ μˆ˜μ •μ‚¬ν•­μ„ λ°˜μ˜ν•΄μ€€λ‹€.


## πŸ‘¨β€πŸ’» TodoItem: JavaScript vs TypeScript
### πŸ” JavaScript
```js
class TodoItem {
constructor(id, task, complete) {
this.id = id;
this.task = task;
this.complete = complete;
}

printDetails() {
console.log(
`${this.id} \t ${this.task} \t ${this.complete ? "(complete)" : ""}`
);
}
}
```


### πŸ” TypeScript
- νƒ€μž…μŠ€ν¬λ¦½νŠΈμ—μ„œλŠ” νƒ€μž…μ„ μ§€μ •ν•΄μ£ΌλŠ”κ±° 이외에 `μ ‘κ·Ό μ§€μ •μž(private, public, protected)` 등을 μ§€μ •ν•  수 μžˆλ‹€.
- μƒμ„±μž(contructor)μ•ˆμ—μ„œ `μ ‘κ·Ό μ§€μ •μž`λ₯Ό μ§€μ •ν•˜λ©΄ λ”°λ‘œ ν”„λ‘œνΌν‹° μ •μ˜ν•  ν•„μš” 없이 ν”„λ‘œνΌν‹°λ‘œ μ •μ˜λœλ‹€.
- ν•¨μˆ˜μ˜ 리턴 값이 μ—†μœΌλ©΄ λ°˜ν™˜ κ°’ νƒ€μž…μ„ `void`둜 μ§€μ •ν•΄μ•Ό ν•œλ‹€.
```ts
class TodoItem {
constructor(public id: number, public task: string, public complete: boolean) {
this.id = id;
this.task = task;
this.complete = complete;
}

printDetails(): void {
console.log(
`${this.id} \t ${this.task} \t ${this.complete ? "(complete)" : ""}`
);
}
}

export default TodoItem;
```


## πŸ‘¨β€πŸ’» TodoCollection
### πŸ” getTodoById λ©”μ„œλ“œ
- ν•¨μˆ˜ λ°˜ν™˜ κ°’μ˜ λŒ€ν•œ νƒ€μž…μ„ `TodoItem | undefined`이런 μ‹μœΌλ‘œ μ§€μ •ν•˜λ©΄ λ°˜ν™˜ κ°’μ˜ νƒ€μž…μ΄ TodoItem λ˜λŠ” undefinedλΌλŠ” 의미
```ts
import TodoItem from "./TodoItem";

class TodoCollection {
...
getTodoById(id: number): TodoItem | undefined {
return this.todoItems.find((item) => item.id === id);
}
...
}

export default TodoCollection;
```


### πŸƒβ€β™‚οΈ Type Annotations - Inference Around Functions
- ν•¨μˆ˜μ˜ νŒŒλΌλ―Έν„°λ₯Ό μ •μ˜ν•  λ•Œ 각 νŒŒλΌλ―Έν„°μ˜ νƒ€μž…μ„ μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ `any` νƒ€μž…μ˜ νŒŒλΌλ―Έν„°κ°€ μ§€μ • λœλ‹€.
- ν•¨μˆ˜μ˜ νŒŒλΌλ―Έν„°μ— νƒ€μž…μ„ μ§€μ •ν•˜μ§€ μ•ŠμœΌλ©΄ 일반 λ³€μˆ˜μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ 암묡적인 any νƒ€μž…μ˜ 적용으둜 κ²½κ³  사항이닀.
- ν•¨μˆ˜μ˜ λ°˜ν™˜ 값에 λŒ€ν•œ νƒ€μž…μ€ return 싀행문에 따라 `νƒ€μž… μΆ”λ‘ (Type Inference)`이 적용 λœλ‹€.
- λ°˜ν™˜ κ°’μ˜ 경우 retrun ꡬ문으둜 `λͺ…μ‹œμ μΈ νƒ€μž…μ˜ μœ μΆ”`κ°€ κ°€λŠ₯ν•˜λ‹€.
- ν•¨μˆ˜μ˜ λ°˜ν™˜ 값이 없을 경우 `void` νƒ€μž…μ˜ λ°˜ν™˜μ„ μ •μ˜ν•œλ‹€.
- ν•¨μˆ˜μ˜ λ°˜ν™˜ κ°’μœΌλ‘œ μ •μ˜ κ°€λŠ₯ν•œ `never` νƒ€μž…μ€ μ ˆλŒ€ λ°œμƒν•˜μ§€ μ•ŠλŠ” κ°’μ˜ νƒ€μž…μ„ λ‚˜νƒ€λ‚Έλ‹€. μ‰½κ²Œ λ§ν•˜λ©΄ λ°˜ν™˜μ„ μ•ˆν•œλ‹€λŠ” 의미
- `void` νƒ€μž…μ€ λ³€μˆ˜λ‘œ μ‚¬μš© 될 경우 `undefined`, `null` κ°’λ§Œ λŒ€μž…(assign)이 κ°€λŠ₯ν•˜λ‹€.
- `never`νƒ€μž…μ€ μ–΄λ–€ λ³€μˆ˜μ˜ λ³€μˆ˜μ—λ„ λŒ€μž… 될 수 μžˆμ§€λ§Œ, never νƒ€μž…μ—λŠ” μ–΄λ–€ νƒ€μž…μ˜ 값도 λŒ€μž…λ  수 μ—†λ‹€.


### πŸƒβ€β™‚οΈ Type Annotations - Tuple
- νŠœν”Œμ„ μ΄μš©ν•˜λ©΄ λ°°μ—΄μ˜ μš”μ†Œ μˆ˜μ™€ 각 μš”μ†Œμ— λŒ€ν•œ νƒ€μž…μ„ μ§€μ •ν•  수 μžˆλ‹€.
- νŠœν”Œμ€ μ •ν•΄μ§„ 길에아 λ§žμ§€ μ•ŠμœΌλ©΄ μ—λŸ¬κ°€ λ°œμƒ. ν•˜μ§€λ§Œ `push()`λŠ” νŠœν”Œμ˜ κ·œμΉ™μ„ λ¬΄μ‹œν•¨.
- μ„œλ‘œ λ‹€λ₯Έ νƒ€μž…μ˜ μš”μ†Œλ₯Ό κ°–λŠ” 배열은 μˆœμ„œμ— 상관없이 데이터λ₯Ό 넣을 수 μžˆμ§€λ§Œ, 반면 νŠœν”Œμ€ μ •ν•΄μ§„ μˆœμ„œμ— 맞게 λ„£μ–΄μ•Ό 함
- νŠœν”Œ νƒ€μž…μ€ 배열보닀 μ €μž₯λ˜λŠ” μš”μ†Œμ— μˆœμ„œμ™€ μˆ˜μ— μ œμ•½μ„ λ‘κ³ μž ν•  λ•Œ μ‚¬μš©
```ts
const tuples: [string, number] = ['Jeon', 27]; //νŠœν”Œ
const arr: (string | number)[] = ['Jeon', 27, 'MinJae', 26]; //λ°°μ—΄

tuples[0] = 'Park' // OK
tuples[0] = 50 // Error: Type '50' is not assignable to type 'string'.ts(2322)

tuples[1] = 50 // OK

tuples.push(100);
console.log(tuples); // ['Park', 50, 100]
```


### πŸƒβ€β™‚οΈ Generics
- μž¬μ‚¬μš© κ°€λŠ₯ν•œ 클래슀, ν•¨μˆ˜λ₯Ό λ§Œλ“€κΈ° μœ„ν•΄ λ‹€μ–‘ν•œ νƒ€μž…μ—μ„œ μ‚¬μš© κ°€λŠ₯ ν•˜λ„λ‘ ν•˜λŠ” 것이 `μ œλ„€λ¦­`이닀.
- μ œλ„€λ¦­μ„ μ΄μš©ν•˜λ©΄ λͺ¨λ“  νƒ€μž…μ˜ 객체λ₯Ό λ‹€λ£¨λ©΄μ„œ 객체 νƒ€μž…μ˜ 무결성을 μœ μ§€ν•  수 μžˆλ‹€.
- μ œλ„€λ¦­μ„ 톡해 ν΄λž˜μŠ€λ‚˜ ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ μ‚¬μš©λ˜λŠ” νŠΉμ • λ°μ΄ν„°μ˜ νƒ€μž…μ„ μ™ΈλΆ€μ—μ„œ μ§€μ •ν•œλ‹€.
- μ œλ„€λ¦­μ΄ 적용된 λŒ€μƒ(클래슀, ν•¨μˆ˜, μΈν„°νŽ˜μ΄μŠ€)은 μ„ μ–Έ μ‹œμ μ΄ μ•„λ‹Œ 생성 μ‹œμ μ— μ‚¬μš©ν•˜λŠ” νƒ€μž…μ„ κ²°μ •ν•œλ‹€.

```ts
class Box {
constructor(private fruit: T) {}
getFruit(): T {
return this.fruit;
}
}
```
- μœ„ μ½”λ“œμ—μ„œ ``을 적어 μ œλ„€λ¦­μ„ 적용 이λ₯Ό νƒ€μž… νŒŒλΌλ―Έν„°λΌκ³  ν•œλ‹€.
- T말고 μ–΄λ– ν•œ μ•ŒνŒŒλ²³μ„ μ‚¬μš©ν•΄λ„ 상관은 μ—†μ§€λ§Œ κ΄€μš©μ μœΌλ‘œ Tλ₯Ό μ‚¬μš©


```ts
const box: Box = new Box(new Orange(5));
```
- μœ„μ— μ½”λ“œλ₯Ό 보면 boxλ₯Ό μ •μ˜ν•˜λ©΄μ„œ box의 νƒ€μž…μœΌλ‘œ Box의 νƒ€μž…μ„ μ œλ„€λ¦­μœΌλ‘œ ``λ₯Ό 지정함 μ‹€μ œ μΈμŠ€ν„΄μŠ€ν™” ν•˜λ©΄μ„œ Orange 객체λ₯Ό μƒμ„±ν•΄μ„œ λ„£μŒ


```ts
class Box {
constructor(private fruit: Orange) {}
getFruit(): Orange {
return this.fruit;
}
}
```
- 결과적으둜 T라고 μ§€μ •λœ νƒ€μž…λ“€μ€ λͺ¨λ‘ Orange νƒ€μž…μ΄ λœλ‹€.


### πŸƒβ€β™‚οΈ type alias
- μƒˆλ‘œμš΄ νƒ€μž…μ„ μ •μ˜ν•˜λŠ” 방법은 type alias와 interfaceλ₯Ό μ •μ˜ν•˜λŠ” 두 κ°€μ§€ 방식이 μžˆλ‹€.
- type aliasλ₯Ό μ΄μš©ν•˜λ©΄ 객체, 곡용체(Union), νŠœν”Œ(Tuple), κΈ°λ³Έ νƒ€μž…μ— νƒ€μž…μ˜ 별칭을 생성할 수 μžˆλ‹€.
- type alias도 μ œλ„€λ¦­μ˜ μ‚¬μš©μ΄ κ°€λŠ₯ν•˜λ©°, 슀슀둜 μ°Έμ‘°ν•˜λŠ” 것도 κ°€λŠ₯ν•˜λ‹€.
```ts
type MyNumber = number;
const n: MyNumber = 10;

type Container = { value: T };

type User = { name: string, age: number };
const testUser: User = { name:"Kim", age:20 };
//=== const testUser: { name: string, age: number } = { ... }
```


## πŸ‘¨β€πŸ’» TodoConsole
### πŸƒβ€β™‚οΈ inquirer 라이브러리 μ„€μΉ˜
- inquirer: Interactive user promptλ₯Ό κ΅¬ν˜„μ„ μœ„ν•œ 라이브러리둜 루비, 파이썬, μžλ°”μŠ€ν¬λ¦½νŠΈ λ“± μ—¬λŸ¬κ°€μ§€ μ–Έμ–΄λ₯Ό μ§€μ›ν•˜κ³  일반적인 μ‚¬μš©μž μž…λ ₯ κ΅¬ν˜„, μ²΄ν¬λ°•μŠ€, λΌλ””μ˜€ λ²„νŠΌ λ“± κ΅¬ν˜„μ΄ νŽΈλ¦¬ν•˜λ‹€.
- 단, TypeScriptμ—μ„œλŠ” @types/inquirer을 μΆ”κ°€μ μœΌλ‘œ μ„€μΉ˜ν•΄μ•Ό ν•œλ‹€.
```
npm i inquirer @types/inquirer
```


### πŸƒβ€β™‚οΈ inquirer μ‚¬μš© 예제
- src/model/TodoConsole.ts
```ts
import * as inquirer from 'inquirer';

class TodoConsole {
...
promptUser(): void {
console.clear();

this.displayTodoList();

inquirer.prompt({
type: 'list',
name: 'command',
message: 'Choose option',
choices: Object.values(Commands),
}).then((answers) => {
if(answers['command'] !== Commands.Quit) {
this.promptUser();
}
});
}
}

export default TodoConsole;
```


- src/model/Command.ts
```ts
export enum Commands {
Quit = 'Quit',
Add = 'Add',
}
```


- μ£Όμ˜ν•  점은 nodemon, concurrently을 μ‚¬μš©ν•΄μ„œ npm startλ₯Ό ν•˜λ©΄ μ•½κ°„μ˜ 였λ₯˜κ°€ 있음
- node build/index.js 둜 λΉŒλ“œ ν•˜κ³  μ‹€ν–‰ν•΄μ•Ό λœλ‹€.


### πŸƒβ€β™‚οΈ enum
- μ—΄κ±°ν˜•(enum) νƒ€μž…μ€ `μƒμˆ˜`듀을 κ΄€λ¦¬ν•˜κΈ° μœ„ν•œ 객체둜 μƒμˆ˜μ˜ 집합을 μ •μ˜ν•œλ‹€.
- 일반 κ°μ²΄λŠ” μ†μ„±μ˜ 변경을 ν—ˆμš©ν•˜μ§€λ§Œ μ—΄κ±°ν˜•μ€ μ†μ„±μ˜ 변경을 ν—ˆμš©ν•˜μ§€ μ•ŠλŠ”λ‹€.
- μ—΄κ±°ν˜•μ˜ 속성은 기본적으둜 `숫자`, `λ¬Έμžμ—΄`만 ν—ˆμš©ν•œλ‹€.
- μ—΄κ±°ν˜•μ„ μ΄μš©ν•˜λ©΄ μƒμˆ˜μ˜ 수λ₯Ό μ œν•œν•  수 있으며 μ½”λ“œμ˜ 가독성을 높일 수 μžˆλ‹€.
```ts
const korean = 'ko';
const english = 'en';
const japanese = 'ja';

type LanguageCode = 'ko' | 'en' | 'ja';

const code: LanguageCode = korean;
```
- μœ„ μ½”λ“œλ₯Ό μˆ˜μ •ν•˜λ©΄
```ts
enum LanguageCode {
korean = 'ko',
english = 'en',
japanese = 'ja',
}

const code: LanguageCode = LanguageCode.korean;
```


## πŸ‘¨β€πŸ’» Union Type/Type Guard
### πŸƒβ€β™‚οΈ Union Type
- TypeScriptλŠ” νƒ€μž…λ“€μ˜ 쑰합을 톡해 μƒˆλ‘œμš΄ νƒ€μž…μ„ μ •μ˜ν•  수 있으며 Union Type도 그쀑 ν•˜λ‚˜μ΄λ‹€.
- Union Type은 νƒ€μž… 선언에 ν•˜λ‚˜ μ΄μƒμ˜ νƒ€μž…μ„ μ§€μ •ν•˜κ³  ν•΄λ‹Ή νƒ€μž… 쀑에 ν•˜λ‚˜μΌ 수 μžˆμŒμ„ λ‚˜νƒ€λ‚Έλ‹€.
- Union Type의 μ •μ˜λŠ” `|` μ—°μ‚°μžλ₯Ό μ΄μš©ν•΄ μ •μ˜ν•œλ‹€.
- Union Type의 멀버 μ‚¬μš©μ€ μ •μ˜λœ λͺ¨λ“  νƒ€μž…μ˜ 곡톡적인 λ©€λ²„λ“€λ§Œ μ‚¬μš©ν•  수 μžˆλ‹€.


### πŸƒβ€β™‚οΈ Type Guard
- Type GuardλŠ” νŠΉμ • μ˜μ—­(블둝) μ•ˆμ—μ„œ ν•΄λ‹Ή λ³€μˆ˜μ˜ νƒ€μž…μ„ ν•œμ •μ‹œμΌœμ£ΌλŠ” κΈ°λŠ₯이닀.
- Union Type의 μ •μ˜λŠ” 각 νƒ€μž…μ΄ κ°–λŠ” 고유 λ©€λ²„λŠ” μ‚¬μš©ν•  수 μ—†λ‹€.
- νŠΉμ • μ˜μ—­μ—μ„œ 각 νƒ€μž…μ΄ κ°–λŠ” 고유 멀버에 λŒ€ν•œ μ‚¬μš©μ€ Type Guardλ₯Ό μ΄μš©ν•©λ‹ˆλ‹€.
- Type GuardλŠ” μ‚¬μš©μžκ°€ μ •μ˜ ν•˜κ±°λ‚˜ `number`, `string`, `boolean`, `Symbol`의 경우 `typeof` μ—°μ‚°μžλ₯Ό μ΄μš©ν•œλ‹€.
```ts
let collection: number[] | string;
collection = 'TypeScript';

collection.split(''); //μ‚¬μš©ν•  수 μ—†μŒ. μ™œ? μˆ«μžλ°°μ—΄μ€ splitμ΄λΌλŠ” λ©”μ„œλ“œλ₯Ό κ°–κ³ μžˆκΈ° μ•ŠκΈ° λ•Œλ¬Έ

//Type Guard
if (typeof collection === 'string') {
//collection이 stringμ΄λΌλŠ”κ²Œ true라면 이 λΈ”λŸ­ μ•ˆμ—μ„œλŠ” split λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.
collection.split('');
}
```