p-iknow / ts-playground

this repo for practicing typescript
1 stars 0 forks source link

decorater 쓸 때 컴파일 에러에 관한 질문 #1

Open ghost opened 4 years ago

ghost commented 4 years ago

background

Cherny, Boris. Programming TypeScript . O'Reilly Media. Kindle Edition. 를 읽고 있습니다.

책 본문의 내용을 간략히 하면 class decorator를 사용하는 경우 하기 명시한 코드와 같이 complie 에러가 나기 때문에 decorator 가 정식 기능이 될 때까지는 다른 방법으로 데코레이터를 대신 하라는 내용입니다.

질문

저는 에러가 나는 이유를 데코레이터가 리턴한 class 의 type 에 대해 typescript 가 infer 하지 못하기 때문이고, infer를 못하는 이유는 데코레이터가 runtime 에 실행되기 때문이라고 이해했는데, 제가 이해한게 맞나요?

코드

type ClassConstructor<T> = new(...args: any[]) => T  

function serializable<
  T extends ClassConstructor<{
    getValue(): Payload 
  }>
>(Constructor: T) { 
  return class extends Constructor { 
    serialize() {
      return this.getValue().toString()
    }
  }
}

@serializable
class APIPayload {
  getValue(): Payload {
    // ...
  }
}

let payload = new APIPayload
let serialized = payload.serialize() // Error TS2339: Property 'serialize' does
                                     // not exist on type 'APIPayload'.

본문 인용

TypeScript assumes that a decorator doesn’t change the shape of the thing it’s decorating — meaning that you didn’t add or remove methods and properties. It checks at compile time that the class you returned is assignable to the class you passed in, but at the time of writing, TypeScript does not keep track of extensions you make in your decorators.

Until decorators in TypeScript become a more mature feature, I recommend you avoid using them and stick to regular functions instead:

let DecoratedAPIPayload = serializable(APIPayload)
let payload = new DecoratedAPIPayload
payload.serialize()                  // string
ghost commented 4 years ago

NOTE  A Property Descriptor is not provided as an argument to a property decorator due to how property decorators are initialized in TypeScript. This is because there is currently no mechanism to describe an instance property when defining members of a prototype, and no way to observe or modify the initializer for a property. The return value is ignored too. As such, a property decorator can only be used to observe that a property of a specific name has been declared for a class. https://www.typescriptlang.org/docs/handbook/decorators.html

ghost commented 4 years ago

https://github.com/babel/babel/issues/9773#issuecomment-477070819

ghost commented 4 years ago

실제 문제는 property initializer 있는 것처럼 보인다. TSC는 클래스 인스턴스에 Object.defineProperty를 추가하지 않고 프로토타입에 decorator만 추가하는 반면, Babel은 그 인스턴스의 property를 정의한다. 그래서, prototype에 decorated된 getter가 정확히 있지만, 인스턴스는 그것에 접근할 수 없다. 내가 보기에 inversifyjs decorator는 initializer 없이 getter와 setter를 가지고 노는 것 같고, 이것은 스펙과 맞지 않는다. eddy 님 코멘트