Post
TS) Enum과 Generic
1. 열거 타입(Enum)
일정 수의 숫자나 문자열로 된 집합
- 숫자형의 열거
// enum 타입으로 요일 정의
enum Weekdays = {
월요일 = 1,
화요일 = 2,
수요일 = 3,
목요일 = 4,
금요일 = 5,
토요일 = 6,
일요일 = 7,
}
let dayOff = Weekday.월요일;
enum은 자동증가 기능이 있다.
enum Weekdays = {
월요일 = 1,
화요일,
수요일,
목요일,
금요일,
토요일,
일요일,
}
console.log(Weekdays[3)= 수요일
enum을 사용하는 이유
function convertTemperature(temp: number, fromTo: string): number {
return ('FtoC' === fromTo) ?
(temp - 32) * 5.0/9.0:
temp * 9.0 / 5.0 + 32;
}
console.log(`70F is ${convertTemperature(70, 'FtoC')}C`);
console.log(`21C is ${convertTemperature(21, 'CtoF')}F`);
console.log(`35C is ${convertTemperature(35, 'ABCD')}F`);
console.log(`35C is ${convertTemperature(35, 'ABCD')}F`);
문자열이지만 잘못된 값인 ‘ABCD’가 매개변수로 들어왔다. 이럴 때 오류를 감지하고 싶다면? enum을 쓰면 된다.
enum Direction {
FtoC,
CtoF,
}
function convertTemperature(temp: number, fromTo: Direction): number {
return Direction.FtoC === fromTo ? (temp - 32) * 5.0/9.0 : temp * 9.0 / 5.0 + 32;
}
console.log(`70F is ${convertTemperature(70, Direction.FtoC)}C`);
console.log(`21C is ${convertTemperature(21, Direction.CtoF)}F`);
- 문자열 열거
enum Direction {
Up = 'UP',
Down = 'DOWN',
Left = 'LEFT',
Right = 'RIGHT'
}
function move(where: Direction){
if(where === Direction.Up {
// move
}
}
move("남쪽"); // Error
enum with redux
enum ProductsActionTypes {
Search = 'Products Search',
Load = 'Porducts Load All',
}
console.log(ProductsActionTypes.Search);
const enum
const enum을 사용하면 자바스크립트가 생성되지 않는다.
제네릭(Generic)
제네릭은 왜 쓰는 것인가?
숫자나 문자열 같은 특정한 타입을 파라미터로 받는 함수를 선언하기는 쉽다.
function calcTax(income: number, state: string){}
타입스크립트를 제네릭을 사용하면 다양한 타입을 지원하는 함수를 작성할 수 있다.
즉, 제네릭을 사용해 함수를 선언하면, 함수의 호출자가 나중에 구체적인 타입을 지정할 수 있다.
타입스크립트는 제네릭 함수, 클래스 또는 인터페이스를 작성할 수 있다.
제네릭 타입은 T in Array
배열 내, 요소 타입을 선언할 때 <> 기호 안에 해당 타입을 작성한다.
class Car{};
const cars = new Array<Car>(10);
// Car 타입인 객체가 10개인 Array 배열을 생성하며, Car[] 타입을 유추할 수 있다.
class Animal {
eatable: boolean;
}
class Cow extends Animal {
weight: number;
}
class Fly {
color: string;
}
const animals: Array<Person> = []; // 제네릭 타입을 선언
animals[0] = new Animal();
animals[1] = new Cow();
animals[2] = new Fly(); // complie error
제네릭의 구조적 타입 시스템
class Animal {
eatable: boolean;
}
const test1 = {eatable: false};
const test2 = new Animal()
console.log(test2 instanceof Animal) // true
console.log(test1 instanceof Animal) // false
const arr:Array<Animal> = [];
arr[0]= test1; // ok
arr[1] = {gogo:true} // compile error
제네릭 타입은 많은 경우에 사용될 수 있다.
다양한 타입의 값을 취하는 함수를 만들 수도 있지만, 호출 중에 구체적인 타입을 명시적으로 작성해야 한다.
클래스, 인터페이스, 함수와 함께 제네릭 타입을 사용하기 위한 작성법이 있다.
// 제네릭 배열
// 배열의 모든 요소가 같다면 value1이 읽고 쓰기 간단
const value1: string[] = ['kim', 'lee'];
const value2: Array<string> = ['kim', 'lee'];
// 배열의 모든 요소가 다르다면 제네릭
const value3: Array<string | number> = ['kim', 'lee', 123];
제네릭 타입 생성
interface Comparator {
compareTo(value: any): number;
}
interface Comparator {
compareTo(value: any): number;
}
class Rectangle implements Comparator {
compareTo(value: any): number{
// logic
}
}
class Triangle implements Comparator {
compareTo(value: any): number {
// logic
}
}
retangle1.compareTo(triangle1); // value 값이 any 이므로 이런 연산이 가능하다.
interface Comparator<T> {
compareTo(value: T): number;
}
class Rectangle implements Comparator<Rectangle> {
constructor(private width: number, private height: number){};
compareTo(value: Rectangle): number{
return this.width * this.height - value.width * value.height;
}
}
const rect1:Rectangle = new Rectangle(2,5);
const rect2: Rectangle = new Rectangle(2,3);
rect1.compareTo(rect2) > 0 ? console.log("rect1 is bigger"):
rect1.compareTo(rect2) == 0 ? console.log("rectangles are equal") :
console.log("rect1 is smaller") ;
class Programmer implements Comparator<Programmer> {
constructor(public name: string, private salary: number){};
compareTo(value: Programmer): number{
return this.salary - value.salary;
}
}
const prog1:Programmer = new Programmer("John",20000);
const prog2: Programmer = new Programmer("Alex",30000);
prog1.compareTo(prog2) > 0 ? console.log(`${prog1.name} is richer`):
prog1.compareTo(prog2) == 0?
console.log(`${prog1.name} and ${prog1.name} earn the same amounts` ) :
console.log(`${prog1.name} is poorer`) ;
제네릭 타입의 기본값
제네릭 타입을 사용하기 위해, 상세 타입을 제공해야 한다.
class A <T> {
value: T;
}
class B extends A { // compile error
}
class B extends A<any>{ // ok
}
다른 방법으로, 제네릭 타입 선언 시 기본 파라미터 타입을 추가하는 방법이 있다.
class A<T = any> {
value T;
}
class B extends A { // ok
}
// 임시 타입도 가능
class A <T = {}> {
value T;
}
제네릭 함수 생성
any 타입을 사용한 함수
function printMe(content: any): any{
console.log(content);
return content;
}
const a = printMe('Hello');
class Person {
constructor(public name:string){}
}
const b = printMe(new Person('Kim'));
제네릭 함수
function printMe <T>(content: T):T {
console.log(content);
return content;
}
const a = printMe('Hello');
class Person {
constructor(public name: string){}
}
const b = printMe(new Person('Kim'));
화살표 함수 내 제네릭 타입 사용
const printMe = <T>(content: T):T => {
console.log(content);
return content;
}
const a = printMe('Hello');
class Person {
constructor(public name: string){}
}
const b = printMe(new Person('Kim'));
const a = printMe<string>('Hello');
함수에 명시적으로
하지만, 타입스크립트 컴파일러는 a의 타입을 문자열로 유추하므로 명시적으로 타입을 사용할 필요는 없다.
(상황마다 케바케일 수 있음)
class Pair <K, V> { // 두 개의 타입 파라미터를 가진 클래스 선언
key: K; // 제네릭 타입 K의 프로퍼티를 선언
value: V; // 제네릭 타입 V의 프로퍼티를 선언
}
class Pair<K, V> {
constructor(public key: K, public value: V) {}
}
function compare <K,V> (pair1: Pair<K,V>, pair2: Pair<K,V>): boolean {
return pair1.key === pair2.key &&
pair1.value === pair2.value;
}
let p1: Pair<number, string> = new Pair(1, "Apple");
let p2 = new Pair(1, "Orange");
// Comparing apples to oranges
console.log(compare<number, string>(p1, p2)); // prints false
let p3 = new Pair("first", "Apple");
let p4 = new Pair("first", "Apple");
// Comparing apples to apples
console.log(compare(p3, p4)); // prints true
console.log(compare(p3, p1)); // compile error 왜 에러가 날까?
interface User {
name: string;
role: UserRole;
}
enum UserRole {
Administrator = 'admin',
Manager = 'manager'
}
function loadUser<T>(): T {
return JSON.parse('{ "name": "john", "role": "admin" }');
}
const user = loadUser<User>();
switch (user.role) {
case UserRole.Administrator: console.log('Show control panel'); break;
case UserRole.Manager: console.log('Hide control panel'); break;
}
고차함수 내 반환 타입 강제
함수를 파라미터로 받거나, 다른 함수를 반환하는 함수를 고차함수라고 한다.
(c: number) => number
(someValue: number) => (multiplier: number) => someValue * multiplier;
고차함수의 사용
const outerFunc = (someValue: number) =>
(multiplier: number) => someValue * multiplier;
const innerFunc = outerFunc(10);
let result = innerFunc(5);
console.log(result);
제네릭을 사용해서 파라미터 타입이 다른 고차함수가 호출되더라도, 동일한 함수 시그니처를 만들어 보자.
type numFunc<T> = (arg: T) => (x: number) => number;
const noArgFunc: numFunc<void> = () => (c: number) => c + 5;
const numArgFunc: numFunc<number> = (someValue: number) =>
(multiplier: number) => someValue * multiplier;
const stringArgFunc: numFunc<string> = (someText: string) =>
(padding: number) => someText.length + padding;
const createSumString: numFunc<number> = () => (x: number) => 'Hello'; //error
요약
- enum 키워드를 통해 제한된 수의 상수들로 이루어진 집합을 선언할 수 있다.
- enum은 숫자 또는 문자열에 이름을 지정할 수 있다.
- const enum은 그 값이 인라인 되면서 자바스크립드가 생성되지 않는다.
- 제네릭은 코드가 실행될 때 적혀있는 다양한 타입의 값을 이용하게 해준다.
- 타입 파라미터를 가진 클래스, 인터페이스, 함수를 작성할 수 있다.