내배캠 75일차

·2023년 1월 27일
0

내일배움캠프

목록 보기
81/142
post-thumbnail

사용한 git hub 주소

Generic types

제네릭은 선언 시점이 아닌 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법입니다.

제네릭을 선언할 때 관용적으로 사용되는 대표적인 식별자로 T가 있고, 그 외에 UV가 있습니다. 반드시 T, U, V를 사용하여 하는 것은 아니지만 관용적인 식별자를 쓰는게 모범적입니다.

어떻게 보면 어떤 타입을 전달해도 사용이 가능한 any랑 다른점이 있을지 고민이 될수도 있지만 any는 타입체크를 전혀 하지 않아서 전달받은 데이터의 타입을 알 수 없고 반환할 때 타입의 정보를 반환하지 않습니다. 이런 반면에 제네릭은 전달받은 타입을 확인 및 반환을 할 수 있고 타입을 제한 할 수도 있습니다.

객체

인터페이스 및 타입에도 제네릭 타입을 적용하여 타입의 프로퍼티에 보다 유연하게 타입을 입힐 수 있습니다.

제네릭 타입에 기초값을 지정할수도 있는데, 기초값이 string으로 지정을 해서 stringObject2 상수의 타입인 IMyInterfaceG<string>을 명시해주지 않아도 됩니다.

// No Generic - object선언할때 타입이 명시가 안됨! 타입추가도 interface에서 해줘야 함
interface IMyInterface {
  value: string | number | Array<string>;
}

const stringObject: IMyInterface = { value: "hello, world!" };
const numberObject: IMyInterface = { value: 1234 };
const stringArrayObject: IMyInterface = { value: ["hello", "world!"] };

// Generic - 반대로 타입을 명시할 수 있고, 타입추가도 따로 안해줘도 되며, 기초값도 줄 수 있음
interface IMyInterfaceG<T = string> {
  value: T;
}

const stringObject2: IMyInterfaceG = { value: "hello, world!" };
const numberObject2: IMyInterfaceG<number> = { value: 1234 };
const stringArrayObject2: IMyInterfaceG<Array<string>> = {
  // IMyInterfaceG<string[]> 기능적으로는 동일
  value: ["hello", "world!"],
};

추가

앞서 69일차의 app.js를 ts로 만들어 볼 때 깨달았던 부분인데 Array<string>string[] 은 기능적으로 차이가 없다!

키워드 사용할 때 조금 다른데 대표적으로 readonly 같은 경우에는 배열 및 튜플 리터럴 형식에서만 사용할 수 있어 에러가 발생한다!

그렇다고 제너릭형식은 readonly 를 사용하지 못하냐? 그건 또 아니다.
ReadonlyArray 라는 유틸리티함수사용하면 에러가 발생하지 않는다!

interface IMyInterface {
  value: string | number | Array<string>;
}

// 기능적으로는 동일
 interface IMyInterface2 {
   value: string | number | string[];
}

// 키워드 사용할 때 조금 다름
// const error: readonly Array<boolean> = [false]; // error
// 'readonly' 형식 한정자는 배열 및 튜플 리터럴 형식에서만 사용할 수 있습니다.
const okay: readonly boolean[] = [true];
// generic과 readonly를 같이 사용하기 위해서는 ReadonlyArray 유틸리티함수사용
const okayGeneric: ReadonlyArray<boolean> = [true];

함수

type User = {
  email: string;
  name: string;
};

function getData<T>(data: T): T {
  return data;
}

// 에러 없이 콘솔로그 되는 유효한 호출
console.log(getData<string>("string data"));
console.log(getData<number>(1234));
console.log(getData<User>({ email: "email@email.com", name: "katie" }));
console.log(getData<string[]>(["string", "data"]));
console.log(getData<string[]>([])); // 빈 배열도 유효한 인자입니다!

Object.keys()/ Object.values()

타입스크립트에서 우리가 자주 사용하는 자바스크립트 메소드가 어떤 타입을 기대하고, 또 어떤 타입을 반환하고 있는지

enum Status {
  Initiated = "Initiated",
  Pending = "Pending",
  Shipped = "Shipped",
  Delivered = "Delivered",
}

interface Order {
  buyer: string;
  orderStatus: Status;
}

// ❌ Type 'string' is not assignable to type 'Status'.
const orders: Order[] = Object.keys(Status).map((status, index) => {
  return {
    buyer: `buyer #${index}`,
    orderStatus: status,
  };
});

❌ Type 'string' is not assignable to type 'Status'.
해결방법
=> Object.keys() 가 string반환
=> keysvalues 로 바꿔주면 Status반환!
Object.values(Status)도 가능

enum Status {
  Initiated = "Initiated",
  Pending = "Pending",
  Shipped = "Shipped",
  Delivered = "Delivered",
}

interface Order {
  buyer: string;
  orderStatus: Status;
}

const orders: Order[] = Object.values(Status).map((status, index) => {
  return {
    buyer: `buyer #${index}`,
    orderStatus: status,
  };
});
console.log(orders);

// const ordersE: Order[] = Object.entries(Status).map((status, index) => {
//   return {
//     buyer: `buyer #${index}`,
//     orderStatus: status,
//   };
// });

class

stack 데이터 구조를 클래스와 인터페이스를 이용하여 구현!

먼저 Stack이라는 클래스에 속성해 있어야 될 주요 함수들을 인터페이스로 정의를 합니다. 그 다음에 클래스를 본격적으로 써볼건데요, 이렇게 먼저 인터페이스를 선언 하고 클래스에 implements 를 해주면 인스턴스 함수들이 오토컴플릿이 되는걸 목격할 수 있습니다.

interface IStack<T> {
  push(item: T): void;
  pop(): T | undefined;
  peek(): T | undefined;
  size(): number;
}

class Stack<T> implements IStack<T> {
  private storage: T[] = [];

  constructor(private capacity = 4) {}
  // constructor : const stack = new Stack<string>(); 을 했을때 실행됨

  push(item: T): void {
    if (this.size() === this.capacity) {
      throw Error("stack is full");
    }

    this.storage.push(item);
  }

  pop(): T | undefined {
    return this.storage.pop();
  }

  peek(): T | undefined {
    return this.storage[this.size() - 1];
  }

  size(): number {
    return this.storage.length;
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.push(3);
numberStack.push(100);

console.log(numberStack.peek()); // 100
console.log(numberStack.size()); // 4

numberStack.push(101); // ❌ Error: stack is full

push(item: T): void 함수는 storage 배열에 인자로 받는 item값을 담아주는 함수입니다. 반환하는 값을 받고싶지 않아서 반환 타입을 void라고 설정을 했습니다. 추가로 capacity라는 private 클래스 상수를 새로운 스택이 형성 될때마다 선언이 되도록 했는데요, push를 했을 때 스택이 수용할 수 있는 제한을 둔겁니다. 그래서 우리가 101을 담으려고 하면 stack is full 이라는 에러를 받습니다. capacity를 설정하지 않고 class를 설립할 수 있으니 원하시면 private capacity = 4를 지우시면 됩니다.

pop() :T | undefinedstorage 배열의 제일 마지막에 위치한 값을 뽑아내는거죠. undefinedstorage가 비어있을 때를 위해 반환값이므로 설정해줍니다.

peek(): T | undefinedpop()과 마찬가지로 storage 배열의 제일 마지막 index에 위치한 값을 다루는 함수인데요, storage 배열이 비어있을수도 있으니 대비해서 undefined를 반환값으로 T와 유니언 타입으로 설정해줍니다.

마지막으로 size(): number 함수는 storage의 배열의 길이를 반환합니다, 즉 숫자를 반환하는 것이죠.

접근 제한자(Access modifier)

// public, private, protected

public => 제한없음, 누구나 access 가능
private => 특정 class 안에서만 access 가능
protected => 특정 class 와 상속받는 class 안에서만 access 가능

class Base {
  first = ""; // 안적으면 자동으로 public
  public second = "";
  protected third = "";
  private fourth = "";

  baseFunction() {
    this.first;
    this.second;
    this.third;
    this.fourth;
  }
}

class Inherited extends Base {
  myFunction() {
    this.first;
    this.second;
    this.third;
    // this.fourth; // error
  }
}

const inherited = new Inherited();

inherited.first;
inherited.second;
// inherited.third; // error
// inherited.fourth; // error
profile
개발자 꿈나무

0개의 댓글