[FE_Bootcamp] 75일차_TypeScript(2)
1. 열거형(Enum)
TypeScript의 열거형(Enum)은 특정 값의 집합을 정의할 때 사용한다. JavaScript에서는 기본적으로 열거형을 지원하지 않지만, TypeScript에서는 문자형 열거형과 숫자형 열거형을 지원한다.
//숫자형 열거형
enum Numbers {
one = 1,
two = 2,
three = 3
}
const oneVal : Numbers = Number.one
const twoVal : Numbers = Numbers[1]
const threeVal : number = Numbers.three
//문자형 열거형
enum Metallica {
vocal = 'James',
guitar = 'Kirk',
bass = 'Robert',
drum = 'Lars'
}
const vocal : Metallica = Metallica.vocal
const guitarlist : Metallica = Metallica[1]
const bassits : string = Metallica.bass
작성 방법은 객체와 거의 비슷하다. 숫자형과 다르게 문자형 열거형은 열거형의 값을 전부 다 특정 문자 또는 다른 열거형 값으로 초기화해야 한다는 특징이 있다.
숫자형 열거형은 key로부터 value에 접근할 수 있고, 반대로 value로부터 key에 접근할 수 있다.
이를 '역매핑'이라고 한다.
2. 인터페이스
TypeScript에서 인터페이스(Interface)는 일반적으로 타입 체크를 위해 사용된다. 인터페이스는 변수, 함수, 클래스에 사용할 수 있으며, 인터페이스에 선언된 프로퍼티 또는 메서드의 구현을 강제하여 일관성을 유지하도록 한다.
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 예약어를 통해 인터페이스를 생성할 수 있다. 또한 함수와 비슷하게 어떠한 프로퍼티를 조건부로 사용하고 싶다면 ? 연산자를 붙여주면 된다.
interface User {
name: string;
age?: number;
}
// 정상 작동
const user: User = {
name: "anna",
age: 20
}
// 정의된 프로퍼티보다 적게 작성했지만 ? 연산자 때문에 정상 작동
const user: User = {
name: "anna"
}
또한 함수의 타입이나 클래스에서도 인터페이스를 사용할 수 있다.
//함수
interface User {
(name : string, age : number) : string
}
const userInfo : User = (name, age) => {
return `${name}은 ${age}세입니다`
}
//클래스
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();
클래스와 비슷하게 인터페이스도 상속이 가능하다
interface Player {
goalkeeper : string;
defender : string;
midfielder : string;
forward : string;
}
interface Team extends Player {
coach : string;
stadium : string;
fans : number;
}
3. 타입 별칭
타입 별칭(Type Aliases)은 타입의 새로운 이름을 만드는 것이다. 이는 새로운 이름으로 기존의 타입을 참조하는 것을 의미한다.
type NewString = string
const name : NewString = 'Song'
type Person = {
id: number;
name: string;
email: string;
}
interface Commentary {
id: number;
content: string;
user: Person;
//Person type 참조
}
이런 방식으로 타입 별칭을 사용하면 코드를 더 간결하고 가독성 좋게 만들 수 있다. 또한 복잡한 타입을 간략하게 표현하고, 타입 정의를 재사용하는 등 가독성을 높이는데도 도움이 된다.
type Person = {
name: string;
age: number;
}
type Students extends Person {
className: string;
}
//에러
type Person = {
name: string;
age: number;
}
interface Students extends Person {
className: string;
}
//정상 작동
type은 interface처럼 사용 가능하다. 다만 type은 type을 상속받을 수는 없으나 interface는 type으로부터 상속받은 프로퍼티를 가지고 새로운 interface를 만들 수 있다.
4. 타입 추론
타입 추론(Type Inference)은 변수나 함수의 타입을 선언하지 않아도 TypeScript가 자동으로 유추하는 기능이다.
let unknown = 'what is this?'
unknown은 타입이 따로 명시되어있지 않지만 값을 보고 TypeScript가 string으로 유추한다.
let unknownArr = [1, 2, null]
위 배열은 number와 null, 2가지의 타입을 가지고 있다.
이런 경우에는 최적 공통 타입 알고리즘을 통해 각 후보의 타입을 고려하여, 모든 후보의 타입을 포함할 수 있는 타입을 선택한다.
이런 타입 추론에는 몇 가지 장점이 있다.
- 코드의 가독성 향상
- 개발 생산성 향상
- 오류 발견 용이성
하지만 무조건 장점만 있는 것은 아니라, 단점 역시 존재한다.
- 타입 추론이 잘못될 경우 코드 오류 발생
- 명시적인 타입 지정이 필요한 경우가 있음
5. TypeScrip의 클래스
TypeScript의 클래스는 JavaScript의 클래스와 비슷하지만 클래스의 속성과 메서드에 대한 타입을 명시할 수 있다.
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 속성은 문자열과 숫자 타입으로 정의되어 있다. TypeScript에서 클래스를 정의할 때는 constructor를 이용하여 초기화하는 멤버들은 전부 상단에서 정의를 해줘야 한다.
또한 contructor 내 인자로 받을 때도 정확히 타입을 명시해 줘야 한다.
Class Baby extends Person {
cry() : void {
console.log('응애!')
}
}
상속 역시 존재한다. Baby클래스는 Person클래스로부터 프로퍼티와 메서드를 상속받고 있다.
이때 Baby 클래스는 파생 클래스 또는 하위클래스(subclasses)라고 한다.
class Mydog {
readonly name: string;
constructor(theName: string) {
this.name = theName;
}
}
let spooky = new Mydog("스푸키");
spooky.name = "멋진 스푸키"; // 에러
readonly 키워드를 사용하여 프로퍼티를 읽기 전용으로 만들 수 있다. 이 읽기 전용 프로퍼티들은 선언 또는 생성자에서 초기화해주어야 한다.
위의 코드는 name이 readonly로 명시되어 있기 때문에, 값을 변경할 수 없다.
이런 식으로 변경되면 안 될 값을 readonly로 명시하여 보호할 수 있다.