JS, TS와 OOP
JS의 변수와 함수는 모두 객체다. 이는 JS의 모든 객체가 프로토타입(prototype)이기 때문이다. 프로토타입 언어란 모든 객체가 최소한 하나 이상의 다른 객체(그들의 프로토타입으로부터) 상속을 받는 언어이다. 이때 상속되는 정보를 제공하는 객체를 프로토타입이라고 한다.
따라서, JS로 객체지향 코드를 구성하려면 애로 사항이 많았다. 이 문제를 해결하기 위해 ES6(ECMAScript 2015)부터는 클래스 문법을 추가하였고, 보다 자바스럽게 클래스를 구성할 수 있게되었다.
클래스
TS의 클래스 문법은 JS와 크게 다르지 않다.
다만, JAVA의 객체 지향 문법을 끌어와서 쓰다보니 JS에는 없는 것까지 사용하면서 몇몇 제약이 생긴다.
JS의 클래스 선언 방식
class Students {
constructor({name, num}) {
this.name = name;
this.num = num;
}
attend() {
return true;
}
}
const student = new Students({name: '세빈', num:2020111});
student.attend();
TS의 클래스 선언 방식
TS에서는 JS와 다르게 인스턴스 변수를 클래스 바디에 선언해준 다음, 생성자 메소드에 this와 정의를 해주어야 한다.
class Students {
name: string;
num: number;
// 생성자를 정의할 때 매개변수에 대한 타입을 명시해야 한다.
constructor({name, num} : {name: string; num:number}) {
this.name = name;
this.num = num;
}
}
TS 클래스의 상속
기본적인 상속 구현
name 프로퍼티를 사용하고, getName 메소드를 통해 name을 출력한다.
class Vehicle {
name: string;
constructor(name: string) {
this.name = name;
}
}
// Vehicle 상속, name properties 사용
class Car extends Vehicle {
getName(): string {
return `${this.name}`;
}
}
let car: Car = new Car('Bentley');
console.log(car.getName());
- 부모 클래스에 정의되어있는 메소드를 오버라이드할 때
class Vehicle {
name: string;
constructor(name: string) {
this.name = name;
}
move(distanceInMeters: number = 0): number {
console.log(`${this.name} moved ${distanceInMeters}m.`);
return distanceInMeters;
}
}
class Boat extends Vehicle {
a: number = 0;
// 생성자 함수는 부모 클래스의 생성자를 실행할 super를 반드시 호출해야 한다.
constructor(name: string) {
super(name);
}
// Override되었기에 타입은 선언해줄 필요없다.
move(distanceInMeters = 5) {
super.move(distanceInMeters);
return distanceInMeters;
}
water() {
console.log("물튄다~");
}
}
let boat: Boat = new Boat('boat');
boat.move();
boat.water();
- 부모 클래스로 타입형을 선언하고 자식 클래스를 생성해서 핥당하면
let v1: Vehicle = new Boat('v1');
v1.move();
v1.water(); // Property 'water' does not exist on type 'Vehicle'.
TS 클래스 접근 제어자
JAVA와 거의 비슷한 접근 제어자 키워드를 사용한다.
다만, TS에는 자바와는 달리 패키지라는 개념이 없기 때문에 default 키워드는 제외되었다. 그래서 TS의 default 값은 public이다.
- public: 어디서나 자유롭게
- protected: 내 자식까지
- private: 나만
- 아이템 56 - 정보를 감추는 목적으로 private 사용하지 않기
- private은 TS의 키워드이기에 이는 컴파일 시점에만 작동하고 런타임 시엔 아무런 영향이 없다. 따라서 private을 선언해도 효력이 없음
생성자 메소드에서 인수 타입 선언과 접근 제어자를 동시에 사용하면 클래스 바디에 직접 프로퍼티를 선언할 필요 없이 바로 사용할 수 있다. ⇒ 코드 간결
class Vehicle {
constructor(public name: string, readonly num: number) {}
getName() {
this.name = name;
}
getAge() {
this.age = age;
}
static / Readonly 키워드
기존의 JS는 프로토타입 언어였기에 클래스 기반 언어와 다른 점이 많아, OOP를 구성하려면 애로사항이 많았다고 앞부분에서 설명을 했다. 이러한 문제점을 해결하기 위해서 ES6에서부터는 클래스를 도입하였는데, 이때 static 키워드가 같이 도입되면서 JS에서도 정적 멤버 생성이 가능해졌다. (JS와 마찬가지로 TS 또한 가능하다.)
아래 예시와 같이 클래스 바디에서 속성의 타입 선언과 같이 사용하다. 하지만 클래스 바디에서 초기화는 할 수 없다. 초기화를 위해서 ① constructor을 사용하거나 ② 직접 초기화를 해주어야 한다.
class Food {
// 직접 초기화
static onion: number = 0;
static tomato: number = 0;
// 생성자로 초기화
constructor() {
Food.onion = 5;
}
static setOnion(n: number): void {
Food.onion = n;
}
}
console.log(Food.onion); // 0
new Food();
console.log(Food.onion); // 5
Food.setOnion(100);
console.log(Food.onion); // 100
readonly: 읽기 전용, 메소드에는 사용할 수 없다. const와 차이점만 기억하면 된다.
- const는 일반변수, readonly는 클래스 내부 변수에 사용
- public static readonly onion: number = 1; 가능
- 아이템 17: 변경 관련된 오류 방지를 위해 readonly 사용하기
인터페이스
자바에서의 인터페이스는 추상 메소드와 상수만을 정의한 클래스 위주로 다루지만, TS의 인터페이스는 객체를 위주로 다룬다는 차이가 있다.
여러 함수가 특정 시그니처를 동일하게 가져야 할 경우, 여러 클래스가 동일한 명세를 정의해야 하는 경우 인터페이스를 통해서 정의한다.
인터페이스에 선언된 프로퍼티/메소드의 구현을 강제하여 각각의 클래스와 함수의 일관성을 유지할 수 있도록 한다.
인터페이스는 TS 문법이기 때문에 컴파일 될 때 JS로 컴파일이 되지 않는다‼️
// 매개변수에서 인터페이스를 타입으로 받을 수 있음.
function check(a: Student) : void {
console.log(`${a.name}s student number is ${a.num}`);
}
check(human);
interface vs type
- 타입
type State1 = {
name: string;
capital: string;
{
- 인터페이스
interface State2 {
name: string;
capital: string;
}
내가 구현하고자 하는 것이 복잡한 타입이라면 → 타입 별칭을 사용한다.
- 인터페이스는 유니온을 할 수 없다.
간단한 객체타입이라면 → 인터페이스를 사용한다.
- 일관성과 확장을 모두 고려할 수 있다.
인터페이스 호환
인터페이스를 같은 이름으로 중복해서 만들 수 있다. 이렇게되면, 같은 이름 내에 선언된 프로퍼티들이 하나로 합쳐진다고 보면 된다. 기존에 만들어진 인터페이스에 내용을 추가하는 경우 자주 쓰인다.
interface FullName {
firstName: string,
lastName: string
}
interface FullName {
middleName: string
}
const myName: FullName = {
firstName: "P",
lastName: "D",
middleName: "S"
}
참고 자료
https://microsoft.github.io/TypeScript-New-Handbook/outline/
'언어 > JS & TS' 카테고리의 다른 글
[TypeScript] 함수 표현식 & Call∙Constructor Signature (1) | 2023.11.29 |
---|---|
[JavaScript] JS의 논리연산자, OR과 AND (0) | 2023.11.28 |
[JavaScript] JS의 this (0) | 2023.11.27 |
[TypeScript] Narrowing | Type Assertions & Type Guards (1) | 2023.11.26 |
[TypeScript] TS 기본 지식 | 정적타입언어, tsc, 타입 (0) | 2023.11.21 |