juni
TS , React Cookbook , 객체 지향 프로그램, 도서 관리 코드 본문
터미널에서 ts-node 사용법 : yarn global add ts-node -> 전역적으로 설치 -> ts-node 파일명
function sum(a: number, b: number): number {
return a + b;
}
function objSum({ a, b }: { a: number; b: number }): string {
return `${a + b}`;
}
type Person = { id: number; age: number; height: number };
async function getPerson(): Promise<Person[]> {
const res = await fetch(`http://localhost:5008/people`);
if (!res.ok) {
throw new Error();
}
return res.json();
// 이렇게 해도 res[0].id 이런식으로 접근가능 함수 타입 안정해도됨
return res.json() as any as Person[];
}
//타입 명시로 속성 접근 가능
getPerson().then((res) => console.log(res[0].age));
리액트앱 TS로 만들기
npx create-react-app 생성할 폴더명 --template typescript
// 제네릭(generic)이란 데이터의 타입(data type)을 일반화한다(generalize)=변수화한다는 것을 의미
type Generic<T> = {
someValue: T;
};
type Test = Generic<string>;
function someFunc<T>(value: T) {}
someFunc<string>("hello");
import React, { useState } from "react";
function App() {
// 타입 명시 없이 값을 넣을 경우(이니셜 값) 값에 맞는 타입으로 자동으로 들어감
const [counter, setCounter] = useState<number>(1);
const increment = () => {
setCounter((prev) => prev++);
};
return <div onClick={increment}>{counter}</div>;
}
export default App;
import React, { useState } from "react";
function Parent() {
// 이니셜값도 맞는 타입으로 할당
const [count, setCount] = useState("");
return <Child count={count}></Child>;
}
type Props = {
count: string;
};
// 타입을 명시안하면 any타입이므로 타입 명시해줘야함
function Child({ count }: Props) {
return <div>{count}</div>;
}
export default Parent;
Props 전달 3가지 예시
import React, { useState } from "react";
type Todo = {
id: string;
isDone: boolean;
};
function App() {
const [todos, setTodos] = useState<Todo[]>([]);
const deleteTodo = (id: string) => {
const newTodos = todos.filter((todo) => todo.id === id);
setTodos(newTodos);
};
return (
<>
{todos.map(({ id }) => (
<Todo key={id} id={id} deleteTodo={deleteTodo} />
))}
</>
);
}
function Todo({
id,
deleteTodo,
}: {
id: string;
deleteTodo: (id: string) => void;
}) {
const handleOnClick = () => {
deleteTodo(id);
};
return <div onClick={handleOnClick}></div>;
}
export default App;
import { PropsWithChildren } from "react";
type BaseType = {
id: string;
};
function Child({ children }: PropsWithChildren<BaseType>) {
return <div>{children}</div>;
}
export function Parent() {
return <Child id=""></Child>;
}
import React, { ReactNode } from "react";
type BaseType = {
id: string;
};
type StrictChildren<T> = T & { children: ReactNode };
function Child({ children }: StrictChildren<BaseType>) {
return <div>{children}</div>;
}
export function Parent() {
return (
<Child id="">
<div>chlidren</div>
</Child>
);
}
유틸리티 타입
// UtilityType.tsx
import {
AddressComponent,
PersonChildComponent,
ProfileComponent,
} from "./UtilityTypeChildren";
export type PersonProps = {
id: string;
description: string;
address: string;
age: number;
profile: string;
};
export const PersonComponent = ({
id,
description,
address,
age,
profile,
}: PersonProps) => {
return (
<>
<PersonChildComponent>
<div>{id}</div>
</PersonChildComponent>
<ProfileComponent
description={description}
address={address}
age={age}
profile={profile}
/>
<AddressComponent address={address} />
</>
);
};
// UtilityTypeChildren.tsx
import { PropsWithChildren, ReactNode } from "react";
import { PersonProps } from "./UtilityType";
export const PersonChildComponent = ({ children }: PropsWithChildren) => {
return <>{children}</>;
};
type OmitType = Omit<PersonProps, "id">;
export const ProfileComponent = ({
description,
address,
age,
profile,
}: OmitType) => {
return <></>;
};
type PickType = Pick<PersonProps, "address">;
export const AddressComponent = ({ address }: PickType) => {
return <></>;
};
Event Handler
import { useState } from "react";
function App() {
const [counter, setCounter] = useState<number>(1);
const eventHandler = (e: React.MouseEvent<HTMLDivElement>) => {};
return <div onClick={eventHandler}>{counter}</div>;
}
export default App;
인터페이스
-
- 인터페이스란?
- 인터페이스는 TypeScript에서 객체의 타입을 정의하는데 사용
- 인터페이스는 객체가 가져야 하는 속성과 메서드를 정의
- 인터페이스를 구현한 객체는 인터페이스를 반드시 준수해야함! 규약과 같아서 어길 수가 없음
- 인터페이스를 사용하면 코드의 안정성을 높이고 유지 보수성을 향상 가능
- ☑️ 구현부 제공 여부
- 추상 클래스
- 클래스의 기본 구현을 제공.
- 인터페이스
- 객체의 구조만을 정의하고 기본 구현을 제공하지 않음
- 추상 클래스
- 단일 상속만 지원
- 인터페이스
- 다중 상속을 지원
- 즉, 하나의 클래스는 여러 인터페이스를 구현 가능
- 추상 클래스
- 추상 클래스를 상속받은 자식 클래스는 반드시 추상 함수를 구현해야 함
- 인터페이스
- 인터페이스를 구현하는 클래스는 인터페이스에 정의된 모든 메서드를 전부 구현해야 함
- 기본 구현을 제공하고 상속을 통해 확장하는데 초점을 맞추고 싶다면 → 추상 클래스
- 객체가 완벽하게 특정 구조를 준수하도록 강제하고 싶다면 → 인터페이스
- 추상 클래스
도서 관리 코드
// 열거형 타입
enum Role {
LIBRARIAN, // 사서
MEMBER, // 멤버
}
// 추상 클래스 -> 공통 속성과 메서드 정의
abstract class User {
// 생성자 함수 -> name age매개변수 받아서 클래스 속성으로 설정
constructor(public name: string, public age: number) {}
// 추상 함수 -> 상속 받는 클래스에서 구현해야함
abstract getRole(): Role;
}
class Member extends User {
constructor(public name: string, public age: number) {
// 부모 클래스 생성자 호출하여 name과 age를 초기화
super(name, age);
}
getRole(): Role {
return Role.MEMBER;
}
}
class Librarian extends User {
constructor(name: string, age: number) {
super(name, age);
}
getRole(): Role {
return Role.LIBRARIAN;
}
}
class Book {
constructor(
public title: string,
public author: string,
public publishedDate: Date
) {}
}
interface RentManager {
getBooks(): Book[];
addBook(user: User, book: Book): void;
removeBook(user: User, book: Book): void;
rentBook(user: Member, book: Book): void;
returnBook(user: Member, book: Book): void;
}
class Library implements RentManager {
private books: Book[] = [];
// rentedBooks는 유저의 대여 이력을 관리
private rentedBooks: Map<string, Book> = new Map<string, Book>();
getBooks(): Book[] {
// 깊은 복사를 하여 외부에서 books를 수정하는 것을 방지
return JSON.parse(JSON.stringify(this.books));
}
addBook(user: User, book: Book): void {
if (user.getRole() !== Role.LIBRARIAN) {
console.log("사서만 도서를 추가할 수 있습니다.");
return;
}
this.books.push(book);
}
removeBook(user: User, book: Book): void {
if (user.getRole() !== Role.LIBRARIAN) {
console.log("사서만 도서를 삭제할 수 있습니다.");
return;
}
const index = this.books.indexOf(book);
if (index !== -1) {
this.books.splice(index, 1);
}
}
rentBook(user: User, book: Book): void {
if (user.getRole() !== Role.MEMBER) {
console.log("유저만 도서를 대여할 수 있습니다.");
return;
}
if (this.rentedBooks.has(user.name)) {
console.log(
`${user.name}님은 이미 다른 책을 대여중이라 빌릴 수 없습니다.`
);
} else {
this.rentedBooks.set(user.name, book);
console.log(`${user.name}님이 [${book.title}] 책을 빌렸습니다.`);
}
}
returnBook(user: User, book: Book): void {
if (user.getRole() !== Role.MEMBER) {
console.log("유저만 도서를 반납할 수 있습니다.");
return;
}
if (this.rentedBooks.get(user.name) === book) {
this.rentedBooks.delete(user.name);
console.log(`${user.name}님이 [${book.title}] 책을 반납했어요!`);
} else {
console.log(`${user.name}님은 [${book.title}] 책을 빌린적이 없어요!`);
}
}
}
function main() {
const myLibrary = new Library();
const librarian = new Librarian("르탄이", 30);
const member1 = new Member("예비개발자", 30);
const member2 = new Member("독서광", 28);
const book = new Book("TypeScript 문법 종합반", "강창민", new Date());
const book2 = new Book("금쪽이 훈육하기", "오은영", new Date());
const book3 = new Book("요식업은 이렇게!", "백종원", new Date());
myLibrary.addBook(librarian, book);
myLibrary.addBook(librarian, book2);
myLibrary.addBook(librarian, book3);
const books = myLibrary.getBooks();
console.log("대여할 수 있는 도서 목록:", books);
myLibrary.rentBook(member1, book);
myLibrary.rentBook(member2, book2);
myLibrary.returnBook(member1, book);
myLibrary.returnBook(member2, book2);
}
main();
- extends: 클래스를 상속받아 기존 클래스의 속성과 메서드를 재사용하고 확장할 때 사용
- implements: 인터페이스를 구현하여 해당 인터페이스가 정의한 메서드와 속성을 반드시 구현하도록 강제할 때 사용
'CS' 카테고리의 다른 글
Next.js (0) | 2024.07.01 |
---|---|
Study_CSS 및 로직 예시 (0) | 2024.07.01 |
TS 객체 리터럴 , 유틸리티 타입 , 간단한 카페 코드, 객체 지향 프로그램 (0) | 2024.06.25 |
TS 타입 및 간단한 성적표 만들기 + tsc 명령어 설명 (0) | 2024.06.24 |
반응형 웹 (1) | 2024.06.16 |