2021. 6. 22. 18:20ㆍ프론트엔드/Typescript
4학년 1학기가 끝났다.
종강을 하고 이틀을 쉰 다음 다시 스터디 카페로 왔다.
이번 방학에 D&D에도 참가하고, 인싸컴도 만들 생각이다.
그리고 급발진하여 구매한 책들도 심심할때마다 읽고 싶다.
여가 시간을 롤 대신 독서나 크래프팅으로 바꾸는게 계획이다.
클래스의 구성요소들 (Class Members)
필드 (Fields)
필드는 다음과 같이 선언하고, 초기값도 만들 수 있다.
class Point {
// 변수명: 자료형; 의 형태로 구성한다.
x: number;
// 변수명: 자료형 = 초기값; 의 형태로 초기값을 설정할 수 있다.
y: number = 0;
// 초기값이 있다면 자동으로 해당 자료형으로 초기화 된다.
// y와 같이 자료형을 임의로 설정하고 싶다면 :를 이용하자
z = 0;
}
--strictPropertyInitialization 옵션을 사용한다면,
더 엄격하게 클래스의 초기값을 설정하도록 할 수 있다.
class GoodGreeter {
name: string;
// 다음과 같이 이름 뒤에!가 있다면 초기값이 없어도 에러가 발생하지 않는다.
// 대신 생성자 이외의 방식으로 초기화가 진행될 때만 사용하자 (에러난다고 막 붙이지 말고)
name2!: string;
constructor() {
// 아래와 같은 초기화 코드가 없다면 에러가 발생한다.
this.name = "hello"
}
}
readonly 키워드를 붙이면 const와 같이 대입연산이 불가능하게 할 수 있다.
readonly가 붙으면 생성자 내에서만 대입을 할 수 있다.
class Greeter {
readonly name: string = "world";
constructor(otherName?: string) {
if (otherName !== undefined) {
this.name = otherName;
}
}
}
생성자 (Constructors)
생성자는 객체가 생성될 때 호출되며, 일반 함수처럼 인자와 초기값, 오버로딩이 가능하다.
class Point {
x: number;
y: number;
constructor(x = 0, y = 0) {
this.x = x;
this.y = y;
}
}
const pt = new Point();
const pt2 = new Point(10, 10);
생성자 내부에서는 super() 키워드를 통하여 부모의 constructor를 호출할 수 있다.
다음의 예시를 보자
class Base {
k = 4;
}
class Derived extends Base {
constructor() {
// super()가 호출되지 않았기 때문에, this 키워드를 사용할 수 없다.
// console.log(this.k);
super();
// 이 위치는 super()가 호출된 이후이기 때문에 가능
console.log(this.k);
}
}
매서드 (Methoeds)
Method는 C++의 멤버함수와 같다. 위의 Field는 멤버변수와 같다.
정확히는 다르지만 개념적으로 같다고 생각하면 된다.
class Point {
x = 10;
y = 10;
scale(n: number): void {
this.x *= n;
this.y *= n;
}
}
여기에서 중요한 점은, 멤버변수에 접근할 때는 this.변수명으로 접근하여야 한다.
예를 들어, x = 10이라고 써버리면 이 파일내의 변수 x에 접근이 되지, 해당 오브젝트의 x에 접근하지 않는다
게터 / 세터 (Getters / Setters)
게터/세터 개념이 왜 생겨났는지 알아보자.
전에는 변수 하나를 만들어도 그에 해당하는 게터세터를 직접 만들었다.
예를 들어 name이라는 변수가 있으면 getName, setName이라는 함수를 만들었다.
이름도 너무 길고, 어차피 만들어야하는데 이렇게 만들 필요가 있나 생각했나보다.
그래서 이렇게 간단한 get / set 키워드를 통하여 사용하게 된다.
class C {
_length = 0;
get length() {
return this._length;
}
set length(value) {
this._length = value;
}
}
타입스크립트에서는 여기에 몇가지 규칙이 추가된다.
1. get이 있지만 set이 없다: 해당 필드는 readonly가 된다.
2. setter의 파라미터의 타입이 설정되어있지 않으면, getter의 반환 type으로 자동 설정된다.
3. getter와 setter는 같은 member visibility를 가진다.
즉, 둘다 public이거나, protected이거나, private여야 한다.
인덱스 시그니처 (Index Signatures)
이전 포스팅에서처럼, []키워드를 이용하여 해당 타입에 맞는 필드를 만들어 낼 수 있다.
하지만 왠만하면 잘 쓰기 어렵고, 쓸 일도 별로 없으니 패스하자.
클래스 상속 (Class Heritage)
인터페이스 상속 (implements)
implements 절을 이용하면 인터페이스를 상속받을 수 있다.
상속받은 클래스에서는 인터페이스의 함수를 선언해야 한다.
또한, Class A implements B, C와 같이 ,를 이용하여 여러 인터페이스를 상속받을 수 있다.
interface Pingable {
ping(): void;
}
interface Pongable {
pong(): void;
}
class Sonar implements Pingable, Pongable {
ping() {
console.log("ping!");
}
pong() {
console.log("pong!");
}
}
interface의 선택적 필드를 상속 받을수도, 안 받을 수도 있다.
interface A {
x: number;
y?: number;
}
// A를 상속했지만 선택적 필드인 y를 상속받지 않은 모습
class C implements A {
x = 0;
}
클래스 상속 (extends)
기존 클래스에 추가 필드, 메서드를 포함하는 클래스를 만들 수 있다.
class Animal {
move() {
console.log("Moving along!");
}
}
class Dog extends Animal {
woof(times: number) {
console.log("woof! * " + times);
}
}
메서드 오버라이딩 (Overriding Methods)
super 키워드를 통하여 부모 클래스의 메서드 중에서 같은 이름의 메서드를 호출할 수 있다.
class Base {
greet() {
console.log("Hello, world!");
}
}
class Derived extends Base {
greet(name?: string) {
if (name === undefined) {
super.greet();
} else {
console.log(`Hello, ${name.toUpperCase()}`);
}
}
}
여기에서 중요한 점은, 함수의 이름과 반환형이 같아야 한다. 파라미터는 바뀔 수 있지만, 부모의 형식을 포함해야 한다.
무슨 말이냐면, 파라미터가 선택적으로 들어갈 수는 있지만 부모의 함수의 형태를 바꿔서는 안 된다.
greet() // 부모 클래스의 함수 원형
greet(name?: string) // 자식 클래스의 함수 오버라이딩 (O)
greet(name: string) // 부모의 원형인 ()를 호출했을 때 처리할 수 없으므로 불가능 (X)
멤버 접근 (Member Visibility)
멤버의 접근 지정자(Access Modifier)라고도 하는,
멤버를 클래스 외부에서 접근할 수 있는가를 설정하는 키워드들이 있다.
public | protected | private | |
클래스 외부에서 접근 가능 | O | X | X |
상속받은 자식에서 접근 가능 | O | O | X |
클래스 내부에서 접근 가능 | O | O | O |
간단하게 설명하면 public은 누구나 접근 가능하고,
protected는 본인과 자식만 접근 가능하며,
private는 자식도 접근할 수 없다.
주의 할 점 (Caveats)
TypeScript의 시스템상, 이러한 접근 지정자들은 타입체킹을 할 때까지만 유효하다.
즉, TypeScript내부에서 컴파일을 할 때만 체크를 해주는거지, Javascript로 컴파일이 되기만 한다면
접근을 제어해주지 않는다
(원래 Javascript에는 필드의 접근 지정자가 없기 때문)
정적 멤버들 (Static Members)
접근 지정자 뒤, 변수명 앞에 static을 붙여 정적인 필드를 만들 수 있다.
물론 static 멤버도 상속과, 접근 지정이 가능하다.
직접 해본 결과, 상속받은 static 매서드를 오버라이딩 하는 것도 가능했다.
class MyClass {
static x = 0;
static printX() {
console.log(MyClass.x);
}
}
console.log(MyClass.x);
MyClass.printX();
정적으로 사용할 수 없는 이름들 (Special Static Names)
Javascript의 Function 프로토타입의 프로퍼티로 쓰이고 있는 이름은 정적으로 사용할 수 없다.
그렇기 때문에 name, length, call는 사용 불가능하다
제너릭 클래스 (Generic Classes)
이전 포스팅인 오브젝트와 함수에서도 봤지만, <>를 이용하여 Type를 처리하는 것이 가능하다.
class Box<Type> {
contents: Type;
constructor(value: Type) {
this.contents = value;
}
}
단, Static Member의 Type을 제너릭이 포함되도록 할 순 없다.
생성자 파라미터로 필드 만들기 (Parameter Properties)
class Params {
constructor(
public readonly x: number,
protected y: number,
private z: number
) {
// No body necessary
}
}
const a = new Params(1, 2, 3);
위와 같이 입력을 받으면, 다음과 같은 코드가 된다.
class Params {
public readonly x: number;
protected y: number;
private z: number;
constructor(x: number, y: number, z: number) {
this.x = x;
this.y = y;
this.z = z;
}
}
클래스 표현 (Class Expressions)
자바스크립트 특성상 개나소나 오브젝트이다보니, 이름 없이 클래스를 만들고 객체에 넣을 수 있다.
const someClass = class<Type> {
content: Type;
constructor(value: Type) {
this.content = value;
}
};
const m = new someClass("Hello, world");
가상 클래스 (Abstract Classes and members)
Typescript에서는 무려 가상 클래스를 지원한다.
즉 자신의 객체를 생성할 수는 없지만, 상속받은 자식 클래스는 객체 생성이 가능하다.
abstract class Base {
abstract getName(): string;
printName() {
console.log("Hello, " + this.getName());
}
}
class Derived extends Base {
getName() {
return "world";
}
}
단, 상속받은 자식 클래스는 부모의 abstract 멤버들을 채워넣어야 한다.
그리고 abstract 클래스는 생성자가 없다.
클래스 간의 상호작용 (Relationships Between Classes)
전의 interface에서도 다루었지만, 구조가 "포함"관계라면 다 인식을 해버린다
https://goldfishdiary.tistory.com/36
그리고 이거는 클래스도 마찬가지다.
class Person {
name: string;
age: number;
}
class Employee {
name: string;
age: number;
salary: number;
}
// OK
const p: Person = new Employee();
출처
https://www.typescriptlang.org/docs/handbook/2/classes.html
'프론트엔드 > Typescript' 카테고리의 다른 글
근황 & 실전 TypeScript 1 (0) | 2021.07.01 |
---|---|
TypeScript - Handbook (Modules) (0) | 2021.06.23 |
TypeScript - HandBook(Object Types) (0) | 2021.06.08 |
TypeScript - Handbook(More on Functions) (0) | 2021.06.01 |
TypeScript - Handbook(Narrowing) (0) | 2021.05.30 |