TypeScript (Enum,Interface,Type,Class)
TypeScript 열거형(Enum)
TypeScript의 열거형은 특정 값의 집합을 정의할 때 사용됩니다. 문자형 열거형과 숫자형 열거형을 지원합니다. 열거형은 숫자형과 문자열형, 혹은 이 둘의 조합으로 정의될 수 있습니다.
열거형은 일반적으로 상수값을 대신하여 사용되므로, 타입스크립트에서는 열거형이 많이 사용됩니다. 열거형은 코드를 더욱 가독성 좋게 만들어주고, 오타와 같은 실수를 방지해 줍니다.
숫자형 열거형(Enum)
디폴트 값으로 숫자형을 사용하며, 다음과 같이 값을 지정하지 않으면 각 값은 자동으로 0부터 시작하여 1씩 증가합니다.
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
console.log(blueValue); // 출력: 4
문자 열거형(Enum)
문자형 열거형은 열거형의 값을 전부 다 특정 문자 또는 다른 열거형 값으로 초기화해야 합니다.
enum Direction {
Up = "UP",
Down = "DOWN",
Left = "LEFT",
Right = "RIGHT",
}
let myDirection: Direction = Direction.Up;
console.log(myDirection); // 출력: "UP"
역 매핑(Reverse mappings)
역 매핑은 숫자형 열거형에만 존재하는 특징입니다. 열거형의 키로 값을 얻을 수 있고, 값으로 키를 얻을 수도 있습니다.
enum Enum {
A
}
let a = Enum.A;
let nameOfA = Enum[a]; // "A"
TypeScript 인터페이스
TypeScript에서 인터페이스는 일반적으로 타입 체크를 위해 사용이 됩니다. 인터페이스는 변수,함수, 클래스에 사용할 수 있으며, 인터페이스에 선언된 프로퍼티 또는 메서드의 구현을 강제하여 일관성을 유지하도록 합니다.
변수와 인터페이스
TypeScript 에서 변수를 선언할 때 인터페이스를 아래와 같이 사용할 수 있습니다. TypeScript에서 인터페이스는 객체의 구조를 정의하기 위해 주로 사용되는 예약어입니다.
interface User {
name: string;
age: number;
}
// 정상적으로 선언됩니다.
const user: User = {
name: "anna",
age: 20
}
위 코드에서는 인터페이스에 정의된 프로퍼티의 순서는 지키지 않아도 되지만, 정의된 프로퍼티는 전부 작성해야 합니다. 하지만 ? 연산자를 사용하면 선택적 프로퍼티를 작성할 수 있습니다.
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);
클래스와 인터페이스
클래스를 구현할 때 인터페이스에서 정의된 함수나 메서드의 매개변수 타입과 반환 값과 일치하도록 구현해야 하므로, 클래스 내부에 해당 메서드의 매개변수 타입을 다시 한번 더 명시해 주지 않으면 컴파일 에러가 발생하게 됩니다.
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
인터페이스와 상속
인터페이스도 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;
}
const food = {} as FoodFreshness;
food.name = "egg";
food.amount = 2;
food.isFreshed = true;
TypeScript 타입 별칭(Type Aliases)
타입 별칭은 타입의 새로운 이름을 만드는 것입니다. 이는 새로운 이름으로 기존의 타입을 참조하는 것을 의미합니다. 타입 별칭을 이용하여 타입의 새로운 이름을 만들 때 키워드 type을 사용하여 작성합니다.
type MyString = string;
let str1: string = 'hello!';
// string 타입처럼 사용할 수 있습니다.
let str2: MyString = 'hello world!';
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",
},
}
인터페이스 vs 타입 별칭
타입 별칭은 말 그래도 타입에 새로운 이름을 부여하는 것에서 그치기 때문에 확장이 되지 않습니다. 그러나 인터페이스는 확장이 가능합니다. 인터페이스는 기존의 인터페이스 및 타입 별칭으로 만들어진 타입 둘 다 상속할 수 있기 때문에, 유연한 코드 작성을 위해서는 인터페이스로 만들어서 필요할 때마다 확장할 수 있습니다.
TypeScript 타입 추론(Type interface)
TypeScript는 정적타입을 지원하는 프로그래밍 언어입니다. 정적타입 시스템을 사용하면 코드의 안정성을 높이고 디버깅을 용이하게 할 수 있습니다. 타입 추론은 변수나 함수의 타입을 선언하지 않아도 TypeScript가 자동으로 유추하는 기능입니다.
최적 공통 타입(Best common type)
TypeScript는 여러 표현식에서 타입 추론이 발생할 때, 해당 표현식의 타입을 사용하여 최적 공통 타입을 계산합니다. 최적 공통 타입은 각 후보 타입을 고려하여, 모든 후보의 타입을 포함할 수 있는 타입을 선택합니다.
// number와 null 을 포함하는 타입이 선택됨
let x = [0, 1, null];
문맥상의 타이핑(Contextual Typing)
문맥상의 타이핑(타입 결정)은 코드의 위치(문맥)을 기준으로 일어납니다.
// 만약 a,b가 number타입이면 반환값도 number로 추론
function add(a, b) {
return a + b;
}
타입 추론의 장단점
장점
- 코드의 가독성 향상
- 개발 생산성 향상
- 오류 발견 용이성
단점
- 타입 추론이 잘못도리 경우 코드 오류 발생
- 명시적인 타입 지정이 필요한 경우가 있음
- 타입 추론만으로 부족한 경우(복잡한 함수나 객체 등)에는 명시적으로 타입 지정이 필요할 수 있습니다.
TypeScript 클래스
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}살 입니다.`);
}
}
const person = new Person('Alice', 30);
person.greet(); // "안녕하세요, 제 이름은 Alice이고, 30살 입니다."
클래스와 상속
extends 키워드를 사용하여 기존에 존재하던 클래스를 상속받아 확장하여 새로운 클래스를 만들 수 있습니다.
class Animal {
move(distanceInMeters: number): void {
console.log(`${distanceInMeters}m 이동했습니다.`);
}
}
class Dog extends Animal {
speak(): void {
console.log("멍멍!");
}
}
public, private 키워드
기본적으로 클래스 내에 선언된 멤버는 외부로 공개되는 것이 기본이지만, public 키워드를 사용해 공개된다고 명시적으로 표시해 줄 수 있습니다. 혹은 외부에 드러내지 않을 멤버가 있다면 private 키워드로 명시해 주면 됩니다.
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}살 입니다.`);
}
}
readonly 키워드
readonly 키워드를 사용하여 프로퍼티를 읽기 전용으로 만들 수 있습니다. 읽기 전용 프로퍼티들은 선언 또는 생성자에서 초기화해야 합니다. readonly 값은 변경할 수 없습니다.
class Mydog {
readonly name: string;
constructor(theName: string) {
this.name = theName;
}
}
let spooky = new Mydog("스푸키");
spooky.name = "멋진 스푸키"; // 에러
TypeScript 제네릭
타입스크립트의 제네릭은 코드 재사용성을 높이고 타입 안정성을 보장하는 기능입니다. 제네릭을 사용하면 함수나 클래스를 작성할 때, 사용될 데이터의 타입을 미리 지정하지 않고, 이후에 함수나 클래스를 호출할 때 인자로 전달된 데이터의 타입에 따라 자동으로 타입을 추론하게 됩니다.
function printLog<T>(text: T): T {
return text;
}
const str = printLog<string>('hello')
// 타입 추론 기능을 활용한 작성 방법
const str2 = printLog('hello')
T는 유저가 준 파라미터의 타입을 캡처하고, 이 정보를 나중에 사용할 수 있게 합니다. 여기에서는 T를 반환 타입으로 다시 사용합니다. 따라서 파라미터와 반환 타입이 같은 타입을 사용하고 있는 것을 확인할 수 있습니다. printLog 함수는 제네릭으로 타입을 불문하고 동작합니다.
인터페이스와 제네릭
interface Item<T> {
name: T;
stock: number;
selected: boolean;
}
const obj: Item<string> = {
name: "T-shirts",
stock: 2,
selected: false
};
클래스와 제네릭
제네릭을 사용하는 TypeScript에서 팩토리를 생성할 때 생성자 함수로 클래스 타입을 참조해야 합니다.
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
제네릭 타입 변수
제네릭에 타입을 줘서 유연하게 함수의 타입을 정의해 줄 수 있습니다.
function printLog<T>(text: T[]): T[] {
console.log(text.length);
return text;
}
// 조금 더 명시적으로 작성
function printLog<T>(text: Array<T>): Array<T> {
console.log(text.length);
return text;
}
제네릭 제약 조건
제네릭에 타입을 줘서 유연하게 함수의 타입을 정의해 줄 수 있습니다.
interface TextLength {
length: number;
}
// 타입을 정의하지 않고도 length 속성 허용하기
function printLog<T extends TextLength>(text: T): T {
console.log(text.length);
return text;
}
interface Item<T> {
name: T;
stock: number;
selected: boolean;
}
// key 키워드를 통한 제약
function printLog<T extends keyof Item>(text: T): T {
return text;
}
printLog('name'); //정상
pirntLog('key'); //에러'프론트엔드 > Section4' 카테고리의 다른 글
| Proxy (3) | 2023.06.07 |
|---|---|
| TypeScript (타입, 함수, 연산자:유니온/인터섹션) (3) | 2023.05.30 |
| UI/UX 디자인 시스템 (2) | 2023.05.24 |
| React Hook (2) | 2023.05.19 |
| Virtual DOM (2) | 2023.05.19 |