오늘은 더 나아가서 TypeScript에 대해서 더 알아보는 시간이다 :)
처음에 배운 내용은 열거형에 대한 내용이였다.
TypeScript의 열거형(Enum)은 특정 값의 집합을 정의할 때 사용된다.
import './style.css';
/* 밑의 코드를 enum으로 구현합니다. */
enum NumberColor {
RED = 0,
GREEN = 1,
BLUE = 2,
}
enum StringColor {
RED = 'red',
GREEN = 'green',
BLUE = 'blue',
}
/* 화면에 나오는 부분 */
const appDiv: HTMLElement = document.getElementById('app');
appDiv.innerHTML = `
<section>
<h1>TypeScript 숫자형 Enum</h1>
<h3 class="red${NumberColor.RED}">${NumberColor.RED}</h3>
<h3 class="green${NumberColor.GREEN}">${NumberColor.GREEN}</h3>
<h3 class="blue${NumberColor.BLUE}">${NumberColor.BLUE}</h3>
</section>
<section>
<h1>TypeScript 문자형 Enum</h1>
<h3 class="${StringColor.RED}">${StringColor.RED}</h3>
<h3 class="${StringColor.GREEN}">${StringColor.GREEN}</h3>
<h3 class="${StringColor.BLUE}">${StringColor.BLUE}</h3>
</section>
`;
위와 같이 작성해보았다 😀
그랬더니 이러한 결과가 깔끔하게 나오는것을 볼 수 있었다.
열거형을 사용하면 오타 같은 실수등을 방지할 수도 있고, 코드의 가독성과 안정성을 높일수도 있다.
다음은 Http 메서드에 열거형을 사용한 방법이다.
enum HttpMethod {
Get = "GET",
Post = "POST",
Put = "PUT",
Delete = "DELETE",
}
function makeRequest(url: string, method: HttpMethod) {
// ...
}
makeRequest("/api/data", HttpMethod.Post);
눈으로 보기 정말 편해지고 오타를 방지하는 점도 좋은것 같다 😆
역 매핑은 숫자형 열거형에만 존재하는 특징이다. 열거형의 키로 값을 얻거나 값으로 키를 얻을수도 있다.
enum Enum {
A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
문자형 열거형에는 존재하지 않는다.
인터페이스는 저번 글에서도 사용한 적이 있는데 더 깊이 알아보도록 하자.
인터페이스는 일반적으로 타입 체크를 위해 사용이 된다고 한다. 변수, 함수, 클래스에 사용할 수 있고, 선언된 프로퍼티 또는 메서드의 구현을 강제하여 일관성을 유지하도록 한다고 한다.
기본적으로 변수를 선언할 때의 인터페이스 이다.
interface User {
name: string;
age: number;
}
// 정상적으로 선언됩니다.
const user: User = {
name: "anna",
age: 20
}
// 프로퍼티의 순서를 지키지 않아도 정상적으로 선언됩니다.
const user: User = {
age: 20,
name: "anna"
}
// 정의된 프로퍼티보다 적게 작성했기 때문에 에러가 납니다.
const user: User = {
name: "anna"
}
// 정의된 프로퍼티보다 많이 작성했기 때문에 에러가 납니다.
const user: User = {
name: "anna",
age: 20,
job: "developer"
}
함수를 사용할 때에는 이렇게 사용한다.
interface User {
name: string;
age: number;
job: string;
}
interface Greeting {
(user: User, greeting: string): string;
}
const greet: Greeting = (user, greeting) => {
return `${greeting}, ${user.name}! Your job : ${user.job}.`;
}
const user: User = {
name: "anna",
age: 30,
job: "developer"
};
const message = greet(user, "Hi");
console.log(message);
원래는 매개변수에도 타입을 지정했지만 인터페이스에 반환타입을 지정했기 때문에 작성하지 않아도 된다.
클래스에서 사용하는 인터페이스는 아직 조금 헷갈린다.. 😅
추후에 다시 배운다고 하니 어떻게 사용하는지만 짚고 넘어가자 😀
interface Calculator {
add(x: number, y: number): number;
substract(x: number, y: number): number;
}
class SimpleCalculator implements Calculator {
add(x: number, y:number) {
return x + y;
}
substract(x: number, y: number) {
return x - y;
}
}
const caculator = new SimpleCalculator();
console.log(caculator.add(4, 9)); //13
console.log(caculator.substract(10, 5)); //5
실습에서는 이런식으로 작성 해보았다!🙄
인터페이스에 상속을 사용할 수 있는 점이 정리하기 편했던것 같다.
타입 별칭을 내가 새로운 이름의 타입을 만드는 것이다.
이것은 기존의 타입을 내가 새로운 이름으로 만든다는 뜻이다.
type MyString = string;
let str1: string = 'hello!';
// string 타입처럼 사용할 수 있습니다.
let str2: MyString = 'hello world!';
이런식으로 작성한다.
이러한 타입별칭은 인터페이스와 같은 특징을 가지고 있지만 서로 다른점이 존재한다.
VSCode로 작성 해보았는데 타입에서는 마우스를 올리면 내부에 있는 프로퍼티들이 보였는데 인터페이스는 보이지 않았다.
그리고 이런 차이점도 존재한다.
type Person = {
name: string;
age: number;
}
interface User {
name: string;
age: number;
}
//에러가 발생합니다.
type Students extends Person {
className: string;
}
//정상적으로 동작합니다.
interface Students extends User {
className: string;
}
//정상적으로 동작합니다.
interface Students extends Person {
className: string;
}
타입은 확장이 되지 않는다. type에서 상속을 사용하려고 하면 에러가 발생했다. 인터페이스는 정상적으로 작동한다. 인터페이스에서 타입을 상속 받는것은 가능했다.
실습에서는 이런식으로 작성해보았다.
인터페이스의 실습과 매우 비슷해서 어렵지 않았다. 😀
TypeScript는 정적타입을 지원하는 프로그래밍 언어이다. 그래서 Type을 무조건 지정해줘야 하는건가..? 하고 생각했지만 타입 추론이라는 기능이 있다고 한다.
타입 추론은 변수나 함수의 타입을 선언하지 않아도 TypeScript가 자동으로 유추해주는 기능이다.
타입 추론은 매우 편리한 기능이지만 장단점이 뚜렷하게 존재한다.
타입 추론의 장단점에 대하여 알아보자 😉
코드의 가독성 향상: 타입 추론을 사용하면 코드의 가독성이 향상됩니다. 명시적으로 타입을 지정하지 않아도 코드에서 변수의 타입을 알 수 있기 때문입니다.
개발 생산성 향상: 타입 추론을 사용하면 코드 작성 시간을 단축할 수 있습니다. 명시적으로 타입을 지정하지 않아도 TypeScript가 자동으로 타입을 추론하기 때문입니다.
오류 발견 용이성: 타입 추론을 사용하면 코드의 오류를 발견하는 것이 쉬워집니다. TypeScript는 변수나 함수의 타입을 추론하여 타입 검사를 수행하기 때문입니다.
타입 추론이 잘못될 경우 코드 오류 발생: 타입 추론은 TypeScript가 자동으로 수행하는 것이기 때문에, 추론이 잘못될 경우 코드 오류가 발생할 수 있습니다.
명시적인 타입 지정이 필요한 경우가 있음: 타입 추론만으로는 부족한 경우가 있습니다. 특히, 복잡한 함수나 객체의 경우에는 명시적인 타입 지정이 필요할 수 있습니다.
나는 타입스크립트에서 타입을 너무 지정하지 않으면 자바스크립트와 그렇게 크게 다른점이 없다는 생각이 든다. 🙄
마지막으로는 내가 어려워했던 타입스크립트의 클래스에 대하여 알아볼 차례이다.. 😅
이번에 확실하게 알아두고 헷갈리지 않도록 조심하자 😉
먼저 타입스크립트의 클래스에서 간단한 예제이다.
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): void {
console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
}
}
먼저 name과 age의 타입을 정의를 했는데 constructor를 이용하여 초기화하는 멤버들은 전부 상단에서 정의를 해주었다.
결과 값을 반환하지 않는 함수는 void 타입을 지정해준 것을 볼 수 있다.
이런 간단한 예제는 다행히 아직 이해하기 쉬운것 같다.
타입스크립트의 클래스에서도 기존에 사용하던 상속을 사용할 수 있다.
이때도 똑같이 extends를 사용하여 상속한다.
class Animal {
move(distanceInMeters: number): void {
console.log(`${distanceInMeters}m 이동했습니다.`);
}
}
class Dog extends Animal {
speak(): void {
console.log("멍멍!");
}
}
const dog = new Dog();
dog.move(10);
dog.speak();
위 예제를 보면 Dog가 Animal을 상속 받고 있다. 따라서 밑에서 dog.move와 dog.speak를 둘다 사용하는 모습이다.
distanceInMeters는 제대로 위에서 number 타입을 지정해준 것을 볼 수 있다.
기본적으로 클래스 내에 선언된 멤버는 외부로 공개되는 것이 디폴트 값이다. 하지만 공개된다고 표시해 줄 수 있다.
이러한 코드 작성을 하면 가독성을 높여줄 수 있다.
class Person {
public name: string;
private age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): void {
console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
}
}
공개는 public 비공개는 private 이다.
readonly를 사용하여 프로퍼티를 읽기 전용으로 만들 수도 있다.
class Mydog {
readonly name: string;
constructor(theName: string) {
this.name = theName;
}
}
let spooky = new Mydog("스푸키");
spooky.name = "멋진 스푸키"; // 에러
위 코드를 보면 name이 읽기 전용이기 때문에 값을 변경할 수 없다.
실습
결과는 2가 나오게 된다.
이런식으로 작성 해보았다.
결과는 제리는 찍찍 톰은 냥냥 하고 울게 된다.
오늘은 타입스크립트에 대해서 더 알아보았다. 코드가 길어지고 많아지면 헷갈리게 될것 같지만 자주 사용하면서 사용법을 익혀야 겠다. 😀