TypeScript 클래스와 인터페이스, 기타

ZeroJun·2022년 6월 27일
0

타입스크립트

목록 보기
2/4

클래스

타입스크립트 클래스의 기본형태는 자바스크립트와 같다. 따라서 자바스크립트와의 차별점을 정리했다.

제네릭

// 클래스<T> 형태로 제네릭 클래스 만들기
class Cup<T> {
  public Content!: T; // 무조건 할당되어 있다고 컴파일러에게 알림 (생성자 null오류 방지)
}

let text = new Cup<string>();
text.Content = "문자열";

let integer = new Cup<number>();
integer.Content = 1_234;

log(`${text.Content}, ${integer.Content}`); // 문자열, 1234

private와 public

// private과 public 접근
namespace FieldInitailizer {
  class Say {
    private message: string = "안녕하세요";

    public hi(): void {
      this.message = "반갑습니다.";
      console.log(this.message);
    }
  }
  const say = new Say();
  // say.message = "또 만나요"; // private멤버 변수에 접근하면 에러발생
  say.hi(); // 반갑습니다.
}

타입스크립트의 생성자에의 매개변수로 private이나 public접근 제한자를 앞에 붙여주면 필드 또는 속성이 자동으로 만들어진다. 이 때 외부 접근이 제한된 private변수는 필드라 칭하고, 접근 가능한 public은 속성이라 칭한다.

// private을 매개변수로 쓰면 필드가 자동으로 만들어진다.
// 아래 nameCard와 nameCard1은 동일하다.
class nameCard {
  // 생성자에서 값을 바당서 자동으로 private name 필드를 생성
  constructor(private name: string) {}
}

class nameCard1 {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

let my = new nameCard("홍길동");
console.log(my.display()); // 이름 : 홍길동

let my = new nameCard1("고길동");
console.log(my.display()); // 이름 : 홍길동
// public도 마찬가지로 매개변수로 쓰면 속성이 자동으로 만들어진다.
class nameCard {
  constructor(public name: string) { // Empty }
  display() {
    return `이름: ${this.name}`;
  }
}

let my = new nameCard("빌게이츠");
console.log(my.display()); // 빌게이츠
my.name = "와우";
console.log(my.display()); // 와우

// 이렇게 public이 쓰인 속성은 외부에서 변경할 수 있다.

가변길이 매개변수

const log = console.log;
// Rest Parameter
function sumAll(...numbers: number[]) {
  let sum: number = 0;
  for (let i = 0; i < numbers.length; i++) {
    sum += numbers[i];
  }
  return sum;
}

log(sumAll(3, 5)); // 8
log(sumAll(3, 5, 7)); // 15
log(sumAll(3, 5, 7, 9)); // 24

타입스크립트 속성, 맴버, get/set

const log = console.log;

// 속성 만들고 사용하기
class Developer {
  public name!: string; // 단순 set및 get;
}

// 클래스의 인스턴스 생성
const developer = new Developer();

// 속성에 값 설정 (set)세터
developer.name = "지영준";
// 속성의 값 조회 (get)게터
console.log(developer.name);

// 전체 속성 사용하기
class Person {
  // 필드
  private _name!: string;

  // 속성
  get name(): string {
    return this._name; // 추가적 작업 가능
  }
  set name(value: string) {
    this._name = value; // 추가적 작업 가능
  }
}

let person = new Person();
person.name = "wow";
console.log(person.name);

class Point {
  // 속성
  public X!: number;
  public Y!: number;

  // 메서드
  Draw(): void {
    console.log(`X : ${this.X}, Y : ${this.Y}`);
  }
}

let point = new Point();
point.X = 100;
point.Y = 200;
point.Draw();

class Employee {
  // age 필드
  private _age!: number;

  // age 속성
  get age() {
    return this._age;
  }

  set age(value: number) {
    if (value > 0 && value < 150) {
      this.age = value;
    } else {
      throw "나이 값이 잘못되었습니다.";
    }
  }
}
const emp: Employee = new Employee();
emp.age = 150;
console.log(emp.age);

Static 맴버 : class내부의 static맴버는 공유 맴버다. 선언과 동시에 할당되기 때문에 클래스.스태틱맴버 이런식으로 접근할 수 있다.

class Car {
  static copyright: string = "test1";
  constructor(public name: string, public maker: string) {}
}

console.log(Car.copyright);

상속

class Parent {
  hi() {
    console.log("부모하이");
  }
}

class Child extends Parent {
  hello() {
    console.log("자식헬로");
  }
}

let child = new Child();
child.hi(); // 부모클래스의 맴버
child.hello(); // 자식클래스의 맴버

추상클래스와 추상 메서드

// 추상클래스
abstract class Shape {
  abstract getArea(): number;
}

// 원 클래스
class Circle extends Shape {
  constructor(public radius: number) {
    super();
  }
  getArea(): number {
    return Math.PI * this.radius ** 2;
  }
}

// 직사각형 클래스
class Rectangle extends Shape {
  constructor(public w: number, public h: number) {
    super();
  }
  getArea(): number {
    return this.w * this.h;
  }
}

// 정사각형 클래스
class Square extends Shape {
  constructor(public size: number) {
    super();
  }
  getArea(): number {
    return this.size ** 2;
  }
}

const circle = new Circle(10);
log(circle.getArea()); // 314.1592

const rectangle = new Rectangle(20, 10);
log(rectangle.getArea()); // 200

const squar = new Square(10);
const area = squar.getArea();
log(area); // 100

인터페이스

인터페이스는 여러 맴버를 주는 엔티티 형식과 멤버를 강제할 수 있는 규약을 정의한다.

  • 속성들(옵셔널 포함) 정의 가능
  • 메서드 정의
  • 인덱서 정의
  • 다른 인터페이스로 확장
  • 기존 자바스크립트의 클래스를 타입스크립트로 옮길때는 interface키워드를 붙이면 됨
  • 개발시에 ts파일에만 존재 (컴파일된 js파일에는 interface키워드가 없음)

인터페이스는 클래스에 포함될 수 있는 속성 또는 메서드에 대한 표준 규약을 제공한다.

기본형태

interface ICar {
  go(): void; // 함수 시그니처만 제공
}

class Car implements ICar {
  go(): void {
    log("인터페이스에 정의된 모든 맴버를 반드시 구현해야 합니다.");
  }
}

let car = new Car();
car.go(); // '인터페이스에 정의된 모든 맴버를 반드시 구현해야 합니다.'

함수 인자에 활용

interface IContact {
  name: string;
  age: number;
}

let theContact = { name: "지영준", age: "29" };

function prointContact(contact: IContact): string {
  return contact.name + contact.age;
}

log(prointContact(theContact));
// age를 문자열로 할당하면 에러 발생
// '{ name: string; age: string; }' 형식의 인수는 'IContact' 형식의 매개 변수에 할당될 수 없습니다.
//  'age' 속성의 형식이 호환되지 않습니다.
//  'string' 형식은 'number' 형식에 할당할 수 없습니다.ts(2345)

//-------------------------------------------------------------------------
  
interface IContact {
  name: string;
  age: number;
  adrr: string;
}

let theContact = { name: "지영준", age: 29 };

function prointContact(contact: IContact): string {
  return contact.name + contact.age;
}

log(prointContact(theContact));
// theContact에 adrr이 없어서 에러 발생
// '{ name: string; age: number; }' 형식의 인수는 'IContact' 형식의 매개 변수에 할당될 수 없습니다.
//  'adrr' 속성이 '{ name: string; age: number; }' 형식에 없지만 'IContact' 형식에서 필수입니다.ts(2345)

//-------------------------------------------------------------------------

interface IContact {
  name: string;
  age: number;
  addr?: string; // Nullable옵션
}

let theContact = { name: "지영준", age: 29 };

function prointContact(contact: IContact): string {
  if (contact.addr) return contact.name + contact.age + contact.addr;
  else return contact.name + contact.age;
}

log(prointContact(theContact)); // 지영준29


//-------------------------------------------------------------------------
// 여러가지 정의 방법
// Entitiy형 정의
// Entitiy형 정의
interface IContact {
  name: string;
  age: number;
  addr?: string; // Nullable옵션
}

let theContact = { name: "지영준", age: 29 };
let theContact2 = { name: "홍길동", age: 25 };
let theContact3 = { name: "김철수", age: 15 };

function prointContact(contact: IContact): string {
  if (contact.addr) return contact.name + contact.age + contact.addr;
  else return contact.name + contact.age;
}

// 함수형 정의
interface IPrintContact {
  (contact: IContact): string;
}

// 반드시 IPrintContact에 따른 매개변수를 구현해야만 한다.
const contactPrinter: IPrintContact = (contact: IContact): string => {
  return contact.name + contact.age;
};

// 배열형 정의
interface IContactArray {
  [idx: number]: IContact;
}

let contactArray: IContactArray;
contactArray = [theContact, theContact2, theContact3];
log(prointContact(contactArray[2])); // 김철수15


// 클래스를 사용하여 묶어서 관리
interface IprintCurrentContact {
  print(): string;
}
class Contact implements IContact, IprintCurrentContact {
  name: string;
  age: number;
  addr: string;
  constructor() {
    this.name = "";
    this.age = 0;
    this.addr = "";
  }
  print(): string {
    return this.name + this.age;
  }
}

const contactTest = new Contact();
contactTest.name = "백두산";
contactTest.age = 100;
log(contactTest.print()); // 백두산100


// 클래스 상속
class ContactWithAddr extends Contact {
  addr: string;
  constructor() {
    super();
    this.addr = "";
  }
  print(): string {
    return this.name + this.age + this.addr;
  }
}
const contactTest2 = new ContactWithAddr();
contactTest2.name = "임꺽정";
contactTest2.age = 400;
contactTest2.addr = "조선";
log(contactTest2.print()); // 임꺽정400조선

튜플 형식

type MultiString = [string, string];
let hello: MultiString = ["hello", "hello2"];

type Multi = [string, number];
let pi: Multi = ["PI", 3.14];

노마드 코더

다형성

제네릭

// call signature 방식으로 다형성 구현
// type SuperPrint = {
//   (arr: number[]):void;
//   (arr: boolean[]): void;
//   (arr: string[]): void;
// }

// number boolean string 등 concrete type이 뭐가 들어올지 모를 때 제네릭을 사용한다.
type SuperPrint = {
  <T>(arr: T[]): T;
};

const superPrint: SuperPrint = (arr) => arr[0];

const a = superPrint([true, false, true]); 
// const superPrint: <boolean>(arr: boolean[]) => void

const b = superPrint([1, 2, 3, 4]); 
// const superPrint: <number>(arr: number[]) => void

const c = superPrint(["a", "b", "c", "d"]); 
// const superPrint: <string>(arr: string[]) => void

const d = superPrint([1, true, "c", "d"]); 
// const superPrint: <string | number | boolean>(arr: (string | number | boolean)[]) => void
// const d: string | number | boolean

이 코드는 일반적인 방법은 아니다.

제네릭의 활용 사례

보통 제네릭은 라이브러리를 만들 때 할용한다. 따라서 라이브러리를 만들거나 다른 개발자가 사용할 기능을 개발하는 경우엔 제네릭이 유용할 것이다.

function superPrint<T>(a : T[]) {
  return a[0];
}

const a = superPrint([true, false, true]); 
// const superPrint: <boolean>(arr: boolean[]) => void

const b = superPrint([1, 2, 3, 4]); 
// const superPrint: <number>(arr: number[]) => void

const c = superPrint(["a", "b", "c", "d"]); 
// const superPrint: <string>(arr: string[]) => void

const d = superPrint([1, true, "c", "d"]); 
// const superPrint: <string | number | boolean>(arr: (string | number | boolean)[]) => void
// const d: string | number | boolean

// 제네릭은 아래와 같이 재사용 할 수 있다.
type Player<E> = {
  name: string;
  extraInfo: E;
};

type NicoExtra = {
  favFood: string;
};

type NicoPlayer = Player<NicoExtra>;

const nico: NicoPlayer = {
  name: "nico",
  extraInfo: {
    favFood: "kimchi",
  },
};

type A = Array<number>;
let a:A = [1,2,3,4];

0개의 댓글