TIL: typescript(class, tsconfig 옵션)

Yeongjong Kim·2022년 1월 20일
0

TIL

목록 보기
5/10

배운 내용 정리하기

typescript


1. class에서 선언한 private(클래스 외부에서 절대 접근 불가), protected(private와 동일하며 + 확장된 클래스까지 접근 가능) 멤버가 있다면, 구조가 동일하더라도 반드시 동일한 클래스를 통해 정의된 멤버여야 같은 타입으로 인정된다. (일반적으로 ts는 구조적으로 동일한 경우 같은 타입으로 인정)

예제 1
class Animal {
  private name: string;
}

class Dog extends Animal {}

class Person {
  private name: string;
}

let animal = new Animal();
let dog = new Dog();
let person = new Person();

animal = dog;
animal = person; /* Type 'Person' is not assignable to type 'Animal'.
Types have separate declarations of a private property 'name'.ts(2322) */

2. strictNullChecks 옵션 설정은 특정 타입으로 지정된 변수에 null과 undefined가 할당되지 못하도록 막는다.

예제 1
declare const loggedInUsername: string;
 
const users = [
  { name: "Oby", age: 12 },
  { name: "Heera", age: 32 },
];
 
const loggedInUser = users.find((u) => u.name === loggedInUsername);
console.log(loggedInUser.age);

위 코드에는 오류가 있습니다. loggedInUsername 식별자는 const키워드와 함께 string 타입으로 annotation 됐습니다. const를 정의할 때는 반드시 초기화가 필요하다는 에러가 발생합니다. 하지만 declare 키워드를 사용했기 때문에 이미 해당 변수가 존재한다고 인식되어 에러가 발생하지 않습니다. 한 파일 내에서 다시 변수에 할당이 불가능하기 때문에 명백한 오류입니다.

위의 오류 속에서 loggedInUsername은 반드시 undefined이며, users.find 메서드도 undefined를 반환할 것입니다. 즉 loggedInUser는 반드시 undefined 이지만 맨 아랫줄의 loggedInUser.age가 실행됩니다.

undefined.age를 실행하면 반드시 type error가 발생합니다. 이를 typescript가 컴파일 단계에서 인지하지 못합니다.

이럴때 strictNullChecks를 true로 설정하면 Null과 undefined타입이 할당돼었을 때 Object is possibly 'undefined'.ts(2532) 이라는 에러 메세지를 출력해 주며 오류를 사전에 방지할 수 있습니다.

예제 2
// strictNullChecks가 false(default)인 경우 Runtime 단계에서 오류가 발생한다.
declare let a: { hello: number } | undefined;
a = undefined;
console.log(a.hello); // TypeError: Cannot read properties of undefined (reading 'hello')

------------------

// strictNullChecks가 true인 경우 Compile 단계에서 오류가 발생한다.
declare let a: { hello: number } | undefined;
a = undefined;
console.log(a.hello); // Object is possibly 'undefined'.ts(2532)

3. strictPropertyInitialization 옵션을 설정하면 class 프로퍼티 타입이 선언된 상태로 초기화 시키지 않았을 때 오류가 발생시킨다. (이 옵션은 strictNullChecks가 활성화 되어야 true로 지정가능)

예제 1
class Animal {
  private name: string;
}

1번 예제를 그대로 가져왔다. Animal 클래스에 name 필드 타입을 annotation했는데 constructor에서 초기화를 하지 않고 있다. 그래도 실행이 된다. 이를 방지하기 위해서 strictPropertyInitialization을 true로 설정해보자. ts(6133) name이 선언되었지만 read.ts에 없다라는 에러가 발생한다. 이어서 ts(2564) 에러가 발생하였고 할당이 되지 않았다고 한다.

에러 메세지: 'name' is declared but its value is never read.ts(6133)
Property 'name' has no initializer and is not definitely assigned in the constructor.ts(2564)

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

위와 같이 반드시 초기화를 해주면 에러 메세지가 사라진다.


4. contructor에 protected를 선언하여 반드시 확장 클래스로 사용하도록 만들기

constructor에 protected를 선언하면 이 함수 또한 자신 클래스 혹은 확장 클래스 내에서만 호출할 수 있습니다. 아래 예시에서 new Person()을 호출하면 외부에서 constructor를 호출하기 때문에 오류가 발생하게 됩니다. 따라서 확장된 클래스 내에서 super 키워드를 통해 호출하도록 유도해야 합니다.

예제
class Person {
  protected name: string;
  protected constructor(name: string) {
    this.name = name;
  }
}

class Employee extends Person {
  private department: string;

  constructor(name: string, department: string) {
    super(name);
    this.department = department;
  }

  public getElevatorPitch() {
    return `Hello, my name is ${this.name} and I work in ${this.department}.`;
  }
}

const p = new Person('John'); /* Constructor of class 
'Person' is protected and only accessible within 
the class declaration.ts(2674) */
const e = new Employee('John', 'Sales');

5. 생성자 매개변수로 한번에 타입 정의하기.

접근 지정자를 생성자 매개변수 앞에 사용하면, 멤버 타입 선언과 동시에 매개변수를 타입도 지정할 수 있다.

예제 1
class Animal {
  protected name: string;
  constructor(name: string) {
   	this.name = name;
  }
}
----------------------------------------
// 짧아진 코드량
class Animal {
  constructor(protected name: string) {
   	this.name = name;
  }
}

6. 추상 클래스

추상 클래스는 interface와 비슷한 면모를 가지고 있다. 차이점은 interface는 타입만 지정할 수 있는 반면, 추상 클래스는 멤버에 대한 구현 세부 정보를 포함할 수 있다. 즉, 일부는 구현을 하고 일부는 타입만 정의할 수 있다는 의미이다.

// interface는 멤버 구현이 불가능하다.
interface Animal {
  makeSound(): void;
  move(): void;
}

// move 멤버 함수를 구현하고 싶은 경우 가능하다.
abstract class Animal {
  abstract makeSound(): void; // 반드시 파생 클래스에서 구현되어야 함.
  move(): void {
    conole.log('roaming the earth...');
  }
}

new Animal() // Cannot create an instance of an abstract class.ts(2511) - 추상 클래스는 스스로 실행될 수 없다.

class Dog extends Animal {
 	 makeSound() {
       console.log("bow bow");
     }
}

중요한 것은, 추상 클래스에서 abstract 키워드를 사용하여 정의한 추상 메서드는 반드시 파상 클래스에서 구현해주어야 한다.

출처:

profile
Front 💔 End

0개의 댓글