코로 넘어져도 헤딩만 하면 그만
Typescript 맛봄 (2) 본문
1. 열거형Enum
2. 인터페이스Interface
3. 타입 별칭Aliases
4. 타입 추론Inference
5. 클래스Class
🚩열거형
흔히 Enum이라고 부르는 열거형에 대해서 알아봅시다. JS에는 없는 기능인데, TS에는 문자형/숫자형 열거형을 지원하고 있습니다. 그래서 열거형이 무엇이냐... 하면 아래와 같은 모습을 하고 있죠.
숫자형 열거형의 예시입니다.
enum Color {
Red,
Green,
Blue,
}
//기본적으로 0부터 시작해서 1씩 증가, 하지만 다음과 같이 수를 지정할 수도 있다.
enum Color {
Red = 1,
Green = 2,
Blue = 4,
}
//아래처럼 산술 계산도 가능합니다.
enum Color {
Red = 1,
Green = 2,
Blue = 4,
}
let c: Color = Color.Green;
let greenValue: number = Color.Green;
let blueValue: number = Color.Blue;
console.log(c); // 출력: 2
console.log(greenValue); // 출력: 2
문자형 열거형은 다음과 같은 모습을 취하는데, 주로 외부에서 가져온 값을 다루기 위해 사용됩니다. HTTP 요청 방식도 열거형으로 표현할 수 있습니다. 열거형은 코드의 가독성을 높이고 오타 같은 실수를 방지하기 위해 씁니다. 숫자형보다 명확한 값이 나와 읽기 편합니다.
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
let myDirection: Direction = Direction.Up;
console.log(myDirection); // 출력: "UP"
//아래와 같이 HTTP를 가능
enum HttpMethod {
Get = "GET",
Post = "POST",
Put = "PUT",
Delete = "DELETE",
}
function makeRequest(url: string, method: HttpMethod) {
// ...
}
makeRequest("/api/data", HttpMethod.Post);
문자열과 숫자열 이넘을 섞어서 사용하는 것은 가능은 하지만, 추천되고 있진 않습니다. 가능하다면 숫자는 숫자만 문자는 문자만 사용하는 걸로 합니다.
+역 매핑
숫자형 열거형에만 존재하는 특징입니다. key로 값을 얻을 수 있고, 값으로 key를 얻을 수 있죠.
enum Enum {
A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
🚩인터페이스
인터페이스는 보통 객체의 구조를 정의하기 위해 사용합니다.
TS의 예약어인 interface를 사용하여 TypeScript 인터페이스를 생성할 수 있다고 해요. 아래처럼 인터페이스를 사용해서 변수를 선언한다면, 인터페이스에 정의를 해준 만큼 써주어야 에러가 나지 않습니다. 순서는 상관 없고요.
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"
}
interface User {
name: string;
age?: number;
}
// 정상적으로 선언됩니다.
const user: User = {
name: "anna"
}
//이렇게 ?를 쓰면 들어오지 않아도 에러가 나지 않는다.
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);
또한 인터페이스는 extends라는 단어를 써서 기존 존재하던 인터페이스를 상속받고, 확장 가능합니다. 훨씬 재사용성을 높이는 방향이 되겠죠.
interface Person {
name: string;
age: number;
}
interface Developer extends Person {
language: string;
}
const person: Developer = {
language: "TypeScript",
age: 20,
name: "Anna",
}
interface FoodStuff {
name: string;
}
interface FoodAmount {
amount: number;
}
interface FoodFreshness extends FoodStuff, FoodAmount {
isFreshed: boolean;
}
//이런 식으로 여러 인터페이스를 상속 받을 수도 있다.
🚩타입 별칭
타입 별칭(Type Aliases)은 새로운 이름으로 기존의 타입을 참조하는 것을 말하며 type을 사용해서 작성합니다.
type Person = {
id: number;
name: string;
email: string;
}
//Commentary 인터페이스에서 Person 타입을 참조하고 있습니다.
interface Commentary {
id: number;
content: string;
user: Person;
}
//객체에서 Commentary 인터페이스를 참조하고 있습니다.
let comment1: Commentary = {
id: 1,
content: "뭐예요?",
user: {
id: 1,
name: "김코딩",
email: "kimcoding@codestates.com",
},
}
코드를 더 간결하게 만드는 데에 type이 도움을 줍니다. 이렇게 만들어진 타입은 내부에 정의된 프로퍼티를 전부 참조해야 하고, 안 그러면 컴파일 에러가 난답니다.
타입 별칭이 인터페이스와 유사한 기능을 수행할 수는 있지만 둘 사이에는 차이점이 있습니다.
🎈타입 별칭 vs 인터페이스?
1.사용목적
인터페이스: 객체의 구조 정의할 때
타입 별칭: 다양한 타입을 이름(닉네임)참조하려고
2. 활용성
인터페이스: 객체를 구성하는 프로퍼티나 메서드 명시하고 유지하는데 유용
타입 별칭: 복잡한 타입 단순화. 공통적으로 사용되는 타입 중복 없이 정의
3. ★ 확장 가능성
인터페이스: extends로 상속,확장 가능
타입: type는 extends가 불가능. 정의하는 순간 끝남.
🚩타입 추론
TypeScript는 타입 추론(Type Inference)이라는 기능을 통해 코드 작성을 도와줍니다. 즉, 변수나 함수의 타입을 선언하지 않아도 자동으로 타입 스크립트가 유추를 해줍니다.
let isNum = 333;
//타입 스크립트는 number타입이라고 추론한다.
이러한 추론을 통해 타입 스크립트는 추론해야 하는 대상의 모든 후보 타입을 포함할 수 있는 '최적 공통 타입'을 계산하고, 그것을 선택합니다. 또한 a와 b의 타입이 모두 number일 때 a+b라는 반환값도 number일 것이라고 추론합니다.
이처럼 자동으로 타입을 추론해주기 때문에 가독성이 향상되며, 생산성과 오류 발견도 용이해집니다. 다만 추론이 잘못될 경우 오류가 발생할 수 있고 명시적으로 타입 지정이 필요한 경우도 있으므로 잘 살펴보아야 합니다.
🎈타입 단언 : 개발자 측에서 직접 단언해주어 검사를 막는다.
const food : FoodFreshness = {} ☞ 이러면 에러가 뜬다.
const food = {} as FoodFreshness ☞ 타입 단언(이 부분은 개발자가 직접 넘겨주니 컴파일러가 타입 검사를 안 해도 됨)
const food = <FoodFreshness> {};처럼 제네틱으로도 표현 가능
🚩클래스
JS에서 지원하는 것과 같이, TS에서도 클래스를 사용해서 객체 생성이 가능합니다. 그러나 TS의 클래스에는 몇 가지 추가된 기능이 존재합니다.
//기존까지 JS로 작성한 class
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
console.log(`안녕하세요, 제 이름은 ${this.name}이고, ${this.age}살 입니다.`);
}
}
//TS로 작성한 class
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}살 입니다.`);
}
}
TS로 작성한 class가 갖는 차이점은, 우선 클래스를 정의할 때, constructor을 사용해서 초기화하는 것들을 전부 상단에 정의해줘야 한다는 부분입니다. 또 constructor에서 인자로 받을 때에도 타입을 명시해주어야 합니다.
클래스는 인터페이스와 마찬가지로, 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();
+ 앞에 readonly를 붙여 명시하면, 이 값은 아예 변경할 수 없게 됩니다.
class Mydog {
readonly name: string;
constructor(theName: string) {
this.name = theName;
}
}
let spooky = new Mydog("Honey");
spooky.name = "HoneyMustard"; // 에러
'CODE STATES 44' 카테고리의 다른 글
CI/CD 및 github action의 workflow (0) | 2023.06.05 |
---|---|
npm ERR! code ENOENT (0) | 2023.06.02 |
"Cannot read properties of undefined (reading 'pathname')" 에러 (0) | 2023.05.31 |
Typescript 맛봄 (1) (0) | 2023.05.30 |
Redux 기초 노마드 코더 정리(2) (0) | 2023.05.29 |