studye / typescript

타입스크립트는 자바스크립트랑 다른 언어인가요?
7 stars 0 forks source link

[Chapter3] Classes #12

Open jongmoon opened 7 years ago

jongmoon commented 7 years ago

개요

class SimpleClass {
  id: number;
  print() : void {
    console.log(`SimpleClass.print() called`);
  }
}

let mySimpleClass = new SimpleClass();
mySimpleClass.print();// result = SimpleClass.print() called

Properties

Implementing interfaces

interface IPrint {
  print();
}

위와 같은 Interface 를 interface 키워드를 이용해 아래와 같이 구현

class ClassA implements IPrint {
  print() {
    console.log(`ClassA.print()`);
  }
}

class ClassB implements IPrint {
  print() {
    console.log(`ClassB.print()`);
  }
}

그래서 무엇이 좋아지는가?

implements 하지 않은 것과 차별점은?

function printClass(a: IPrint) {
  a.print(); // a 가 어떤 구현체이던 상관없이 실행 가능
}

let classA = new ClassA();
let classB = new ClassB();

printClass(classA);
printClass(classB);

Constructor

class ClassWithConstructor {
  id: number;
  name: string;
  // Class 생성시 파라미터를 받을 수 있음
  constructor(_id: number, _name: string) {
    this.id = _id;
    this.name = _name;
  }
}
// 아래와 같이
var classWithConstructor = new ClassWithConstructor(1, "name");

function

함수의 기능

class ComplexType implements IComplexType {
  id: number;
  name: string;
  // 함수 오버로드 가능
  constructor(idArg: number, nameArg: string);
  constructor(idArg: string, nameArg: string);
  // constructor(idArg: boolean, nameArg: string); 를 해주지 않으면 true/false 지정 시 에러 (v2.4.1)
  constructor(idArg: any, nameArg: any) {
    if (typeof idArg === "number") {
      this.id = idArg;
    }
    this.name = nameArg;
  }

  // 타입 지정
  print(): string {
    let ret = "id:" + this.id + " name:" + this.name;
    console.log(ret);
    return ret;
  }

  // any 키워드를 사용하여 어떤 타입이든 허용가능
  usingTheAnyKeyword(arg1: any): any {
    this.id = arg1;
  }

  //옵셔널한 파라미터를 가질 수 있음
  usingOptionalParameters(optionalArg1?: number) {
    if (optionalArg1) {
      this.id = optionalArg1;
    }
  }

  //파라미터 기본값을 가질 수 있음
  useDefaultParameters(defaultArg1: number = 0) {
    this.id = defaultArg1;
  }

  //rest 문법 사용 가능 (for 배열)
  usingRestSyntax(...argArray: number[]) {
    if (argArray.length > 0) {
      this.id = argArray[0];
    }
  }

  //함수를 파라미터로 받을 수 있음
  usingFunctionCallbacks(callback: (id: number) => string) {
    callback(this.id);
  }
}

Interface function definition

constructor 의 경우 특이사항이 있다.

IComplexType 이 다음과 같이 constructor 를 가지고 있는 경우 에러를 유발한다.

interface IComplexType {
  id: number;
  name: string;
  constructor(arg1: any, arg2: any);
};

// 에러 발생 : Class 'ComplexType' incorrectly implements interface 'IComplexType'.
//  Types of property 'constructor' are incompatible.

생성자가 호환되지 않는다?

Class modifier

3가지 modifier

Constructor access modifier

TypeScript 에서 새로운 문법(?)

생성자에서 access modifier를 지정 가능할 수 있는 shorthand 문법

class classWithAutomaticProperties {
  // 아래와 동일한 효과를 갖는다.
  // public id: number;
  // private name: string;

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

let myAutoClass = new classWithAutomaticProperties(1, "className");
console.log(`myAutoClass id: ${myAutoClass.id}`);
console.log(`myAutoClass name: ${myAutoClass.name}`);//그래서 에러가 발생:'Property 'name' is private and only accessible within class 'classWithAutomaticProperties'.'

저자의 의견

Readonly properties

class ClassWithReadOnly {
  readonly name: string;
  constructor(_name: string) {
   // readonly 변수를 설정할 수 있는 유일한 곳은 생성자
    this.name = _name;
  }
  setReadOnly(_name: string) {
    // 컴파일 오류
    this.name = _name;
  }
}

Class property accessors

property accessors 는 ES5 에 소개된 개념

class ClassWithAccessors {
  private _id: number;
  get id() {
    console.log(`inside get id()`);
    return this._id;
  }

  set id(value) {
    console.log(`inside set id()`);
    this._id = value;
  }
}
var classWithAccessors = new ClassWithAccessors();
classWithAccessors.id = 2;
console.log(`id property is set to ${classWithAccessors.id}`);

//결과
// inside set id()
// inside get id()
// id property is set to 2

주의사항

Static functions

인스턴스 생성없이 Class 이름으로 접근가능한 함수

class StaticClass {
  static printTwo() {
    console.log(`2`);
  }
}

StaticClass.printTwo();// 함수 호출 시 인스턴스가 아닌 Class 이름으로 prefix한다.

Static properties

함수 뿐만아니라 properties 도 static 속성 지정가능

class StaticProperty {
  static count = 0;
  updateCount() {
    StaticProperty.count++;
  }
}

Namespaces

동일한 이름을 갖는 서로 다른 라이브러리와의 충돌을 피하기 위한 일반적인 방법

namespace FirstNameSpace { // namespace 를 통해 scope 를 제한하고,
  class NotExported {
  }

  export class NameSpaceClass {// export 키워드 뒤에 있는 Class 만 Visible 하게 한다.
    id: number;
  }
}

// 사용방법
let firstNameSpace = new FirstNameSpace.NameSpaceClass();
let notExported = new FirstNameSpace.NotExported();// 에러 발생: Property `NotExported` does not exist on type `typeof FirstNameSpace`
junthus commented 7 years ago

추가 - Constructor access modifier

constructor args 에 modifier 를 지정하지 않으면 this 에 바인딩 되지 않는... 숨겨진 그지같은 룰이 있습니다.

denzels commented 7 years ago

이거 반대아닌가요?
원래 consturctor args는 개발자가 직접 this에 바인딩 해줘야 하는데 modifier를 붙히면 typescript가 자동으로 binding 해 줌. ㅋㅋㅋ

junthus commented 7 years ago

아 그렇게 순서를 바꾸니 그럴싸해보이네여... ㅋㅋㅋㅋㅋ

jongmoon commented 7 years ago

modifier 를 붙이면 그냥 일반 파라미터로 취급하는거 인게죠~ 다른 언어들 처럼 (모르면서 막 아는척...) 전 오히려 modifier 를 붙였을때 this 에 바인딩해주는게 좀 이상한 용법처럼 느껴지더라구요.

denzels commented 7 years ago

난 이게 어색하긴 한데... 괜찮다고 생각함. 대게 생성자로 받은 값을 instance 변수에 할당해서 사용하는 경우가 많다보니...

modifier를 명시적으로 붙여야만 처리를 해주는 것도 개발자가 그러한 점을 인지하고 modifier 설정을 해줬으니 내가 처리해 줄께 라는 점에서... 괜찮은 듯... ( private, public, protected가 어떤 제한을 설정한 건지를 생각해보면... )

그리고 어차피 안 붙히면 처리를 안하니 호환성에 문제도 없고...