TypeScript - inherence, interface

이소라·2022년 6월 4일
0

TypeScript

목록 보기
4/28

Inherence

Class Inherence

  • 클래스는 하나의 클래스만 상속 받을 수 있음
  • extends 키워드를 사용하여 상위 클래스를 상속 받음
class Department {
  private employees: string[] = [];
  
  constructor(private readonly id: string, public name: string) {
    this.name = n;
  }

  describe(this: Department) {
    console.log('pDepartment: ' + this.name);
  }

  addEmployee(employee: string) {
    this.employees.push(employee);
  }

  printEmployeeInfo() {
    console.log(this.employees.length);
    console.log(this.employees);
  }
}


class ITDepartment extends Department {}

const accounting = new ITDepartment('d1', 'accounting')
  • 하위 클래스에 고유 생성자를 추가할 때, 먼저 super를 호출해서 실행해야함
    • super() : 상속받은 클래스의 생성자를 호출함
class ITDepartment extends Department {
  constructor(id: string, public admins: string[]) {
    super(id, 'IT');
    this.admins = admins;
  }
}

const it = new ITDepartment('d2', ['Max']);
  • 하위 클래스에서 상위 클래스의 메소드를 덮어 쓸 수 있음
  • 하위 클래스에서 접근 가능하도록 상위 클래스의 member를 protected로 선언해야함
class Department {
  // private을 protected로 변경함
  protected employees: string[] = [];

  constructor(private readonly id: string, public name: string) {
  }

  addEmployee(employee: string) {
    this.employees.push(employee);
  }
}

class AccountingDepartment extends Department {
  constructor(id: string, private reports: string[]) {
    super(id, 'Accounting');
  }
  // Department의 addEmployee 메소드를 overriding함
  addEmployee(name: string) {
    if (name === 'Max') {
      return;
    }
    this.employees.push(name);
  }
}

Getter & Setter

Getter

  • 값을 가져올 때 함수나 메소드를 실행하는 속성
  • getter 생성 방법 : get getterName() { return value; }
  • getter는 항상 반환값을 가짐
  • getter을 메소드로 실행시키는 것이 아니라 일반 속성처럼 접근해서 사용함
class AccountingDepartment extends Department {
  private lastReport: string;

  get mostRecentReport() {
    if (this.lastReport) {
      return this.lastReport;
    }
    throw new Error('No report found.');
  }

  constructor(id: string, private reports: string[]) {
    super(id, 'Accounting');
    this.lastReport = reports[0];
  }

  addReport(text: string) {
    this.reports.push(text);
    this.lastReport = text;
  }
}

const accounting = new AccountingDepartment('d2', []);
// Error, reports가 빈 배열이므로
console.log(accounting.mostRecentReport);
accounting.addReport('Something went wrong...');
// OK, 'Something went wrong...'
console.log(accounting.mostRecentReport);

Setter

  • 값을 설정할 때 함수나 메소드를 실행하는 속성
  • setter 생성 방법 : set setterName(value) {}
  • getter을 메소드로 실행시키는 것이 아니라 일반 속성처럼 접근해서 사용함
class AccountingDepartment extends Department {
  private lastReport: string;

  set mostRecentReport(value: string) {
    if (!value) {
      throw new Error('Please pass in a valid value!');
    }
    this.addReport(value);
  }

  constructor(id: string, private reports: string[]) {
    super(id, 'Accounting');
    this.lastReport = reports[0];
  }
  
  addReport(text: string) {
    this.reports.push(text);
    this.lastReport = text;
  }
}

const accounting = new AccountingDepartment('d2', []);
// Error, ''는 false이기 때문에
accounting.mostRecentReport = '';
// OK, 'Year End Report'가 this.reports에 추가됨
accounting.mostRecentReport = 'Year End Report';

Static Property & Method

  • 정적 속성과 메소드를 사용하여, 클래스의 인스턴스에서 접근할 수 없는 속성과 메소드를 클래스에 추가할 수 있음
  • 정적 속성과 메소드는 클래스에서 직접 접근함
  • 논리적으로 그룹화하거나 클래스에 매칭하는 util 함수나 클래스의 전역 상수에 사용됨
  • 정적 메소드 생성 방법 : static methodName(params) { return MappedObject; }
  • 정적 속성 생성 방법 : static variableName = value;
class Department {
  static fiscalYear = 2020;
  protected employees: string[] = [];
  
  constructor(private readonly id: string, public name: string) {
    this.name = n;
  }

  static createEmployee(name: string) {
    return {name: name};
  }
}

const employee1 = Department.createEmployee('Max');
console.log(employee1) // { name: 'Max'}
console.log(Department.fiscalYear) // 2020

  • 정적 속성과 메소드은 인스턴스에서 유효하지 않음
    • this 키워드를 사용하여 접근할 수 없음
    • 클래스 이름을 사용하면 클래스 내부에서도 접근 가능함
  • 생성자를 static으로 변경할 수 없음

Abstract Class

  • 상위 클래스에서 빈 메소드를 기본 클래스를 정의하고, 하위 클래스에서 이 메소드를 재정의하여 사용함
  • abstract 키워드를 붙임
  • 추상 메소드는 추상 클래스 내부에서만 존재할 수 있음
  • 추상 메소드는 모든 하위 클래스 내에서 존재해야함
  • 상위 클래스의 속성이나 메소드를 모든 하위 클래스에서 공유하려고 할 때 유용함
  • 추상 클래스는 자체적으로 인스턴스화할 수 없음
abstract class Department {
  static fiscalYear = 2020;
  protected employees: string[] = [];
  
  constructor(private readonly id: string, public name: string) {
    this.name = n;
  }
  
  abstract describe(this: Department): void;
}

class ITDepartment extends Department {
  constructor(id: string, public admins: string[]) {
    super(id, 'IT');
    this.admins = admins;
  }
  
  describe() {
    console.log(`IT Department: ${this.id}`);
  }
}

Private Constructor

  • 싱글톤 패턴 : 특정 클래스의 인스턴스를 하나만 가짐
  • constructor 앞에 private 키워드를 붙여서 만듬
class AccountingDepartment extends Department {
  private lastReport: string;
  private static instance: AccountingDepartment;

  private constructor(id: string, private reports: string[]) {
    super(id, 'Accounting');
    this.lastReport = reports[0];
  }
  
  static getInstance() {
    if (AccountingDepartment.instance) {
      return this.instance;
    }
    
    this.instance = new AccountingDepartment('d2', []);
    return this.instance;
  }
}
// Error
const accounting = new AccountingDepartment('d2', []);
// OK
const accounting = AccountingDepartment.getInstance();
const accounting1 = AccountingDepartment.getInstance();
console.log(accounting === accounting1) // true

Interface

interface

  • interface는 객체의 구조를 설명함
  • interface에서는 값을 초기화할 수 없음
  • 객체의 타입을 확인하는데 사용됨
interface Person {
  name: string;
  age: number;
  greet(phrase: string): void; 
}

let use1: Person;

user1 = {
  name: 'Max',
  age: 40,
  greet: (phrase: string) {
    console.log(phrase + ' ' + this.name);
  }
}

user1.greet('Hi there, I am') // 'Hi there, I am Max'

Interface & Class

  • interface를 준수하는 클래스는 interface와 같은 구조를 가짐
  • interface를 준수하는 클래스 생성 방법 : Class ClassName implements interfaceName {}
interface Greetable {
  name: string;
  greet(phrase: string): void; 
}

Class Person implements Greetable {
  name: string;
  
  constructor(n: string) {
    this.name = n;
  }
  
  greet(phrase: string) {
    console.log(phrase + ' ' + this.name);
  }
}
  • interface를 준수하는 클래스에 메소드나 필드를 추가 가능함
interface Greetable {
  name: string;
  greet(phrase: string): void; 
}

Class Person implements Greetable {
  name: string;
  age: number = 30;
  
  constructor(n: string) {
    this.name = n;
  }
  
  greet(phrase: string) {
    console.log(phrase + ' ' + this.name);
  }
}

const user1 = new Person('Max');
console.log(user1); // Person {age: 30, name: "Max"}
  • interface는 구체적인 구현이 아니라 서로 다른 클래스 간의 기능을 공유하기 위해 사용됨

  • interface를 타입으로 정의할 수 있음

interface Greetable {
  name: string;
  greet(phrase: string): void; 
}

Class Person implements Greetable {
  name: string;
  age: number = 30;
  
  constructor(n: string) {
    this.name = n;
  }
  
  greet(phrase: string) {
    console.log(phrase + ' ' + this.name);
  }
}

let user1 : Greetable;
const user1 = new Person('Max');
console.log(user1); // Person {age: 30, name: "Max"}

Readonly Interface

  • interfacereadonly 키워드를 추가할 수 있음 (public, private은 지정 불가능)
  • 모든 객체의 속성이 한번만 설정되어야 하며, 그 이후에는 읽기만 가능함
readonly interface Greetable {
  name: string;
  greet(phrase: string): void; 
}

Class Person implements Greetable {
  name: string;
  age: number = 30;
  
  constructor(n: string) {
    this.name = n;
  }
  
  greet(phrase: string) {
    console.log(phrase + ' ' + this.name);
  }
}

let user1 : Greetable;
const user1 = new Person('Max');
console.log(user1); // Person {age: 30, name: "Max"}
  • 타입에도 readonly 키워드 추가 가능함
interface Greetable {
  readonly name: string;
  greet(phrase: string): void; 
}

Class Person implements Greetable {
  name: string;
  age: number = 30;
  
  constructor(n: string) {
    this.name = n;
  }
  
  greet(phrase: string) {
    console.log(phrase + ' ' + this.name);
  }
}

const user1 = new Person('Max');
// Error, Greatable interface로부터 name 속성이 readonly라는 것을 추론
user1.name = 'Manu';

Interface Inherence

  • extends 키워드를 사용하여 interface를 상속함
interface Named {
  readonly name: string;
}

interface Greetable extends Named {
  greet(phrase: string): void; 
}

Class Person implements Greetable {
  name: string;
  age: number = 30;
  
  constructor(n: string) {
    this.name = n;
  }
  
  greet(phrase: string) {
    console.log(phrase + ' ' + this.name);
  }
}

const user1 = new Person('Max');
// Error, Greatable interface로부터 name 속성이 readonly라는 것을 추론
user1.name = 'Manu';
  • 여러 interface는 쉼표(,)로 구분하여 한 클래스에 여러 개 interface를 합칠 수 있음
interface Greetable {
  name: string;
  greet(phrase: string): void; 
}

Class Person implements Greetable, AnotherInterface {
  ...
}

Interface for Function type

  • interface interfaceName { {params: paramsType}: returnType }
interface AddFn {
  {a:number, b:number}: number;
}

let add: AddFn;

add = (n1: number, n2: number) => {
  return n1 + n2;
}

Optional Property of Interface

  • 물음표(?)를 추가하여 선택적 속성을 지정할 수 있음
interface Named {
  readonly name: string;
  outputName?: string;
}

Class Person implements Greetable {
  // outputName 없어도 no Error
  name: string;
  age: number = 30;
  
  constructor(n: string) {
    this.name = n;
  }
  
  greet(phrase: string) {
    console.log(phrase + ' ' + this.name);
  }
}
  • interface를 준수하는 클래스의 속성이 선택적이라면, interface의 속성도 선택적이어야함
interface Named {
  readonly name?: string;
}

Class Person implements Greetable {
  name?: string;
  age: number = 30;
  
  constructor(n?: string) {
    if (n) {
     this.name = n; 
    }
  }
  
  greet(phrase: string) {
    if (this.name) {
      console.log(phrase + ' ' + this.name);
    } else {
      console.log('Hi');
    }
  }
}
// No Error
const user1 = new Person();

interface in JavaScript file

  • JavaScript 파일에 interface는 관련된 내용은 트랜스파일링되지 않음

0개의 댓글