首页 > 解决方案 > 未调用 TypeScript 装饰器和私有类方法

问题描述

我刚开始学习 TypeScript,我正在制作一个简单的“添加项目”应用程序,我按照教程进行操作

就是这个index.html文件,不多说这个文件,因为它只是通过idHMTL 元素来操作 DOM 的地方

这是app.css文件:

* {
  box-sizing: border-box;
}

html {
  font-family: sans-serif;
}

body {
  margin: 0;
}

label,
input,
textarea {
  display: block;
  margin: 0.5rem 0;
}

label {
  font-weight: bold;
}

input,
textarea {
  font: inherit;
  padding: 0.2rem 0.4rem;
  width: 100%;
  max-width: 30rem;
  border: 1px solid #ccc;
}

input:focus,
textarea:focus {
  outline: none;
  background: #fff5f9;
}

button {
  font: inherit;
  background: #ff0062;
  border: 1px solid #ff0062;
  cursor: pointer;
  color: white;
  padding: 0.75rem 1rem;
}

button:focus {
  outline: none;
}

button:hover,
button:active {
  background: #a80041;
  border-color: #a80041;
}

.projects {
  margin: 1rem;
  border: 1px solid #ff0062;
}

.projects header {
  background: #ff0062;
  height: 3.5rem;
  display: flex;
  justify-content: center;
  align-items: center;
}

#finished-projects {
  border-color: #0044ff;
}

#finished-projects header {
  background: #0044ff;
}

.projects h2 {
  margin: 0;
  color: white;
}

.projects ul {
  list-style: none;
  margin: 0;
  padding: 1rem;
}

.projects li {
  box-shadow: 1px 1px 8px rgba(0, 0, 0, 0.26);
  padding: 1rem;
  margin: 1rem;
}

.projects li h2 {
  color: #ff0062;
  margin: 0.5rem 0;
}

#finished-projects li h2 {
  color: #0044ff;
}

.projects li h3 {
  color: #575757;
  font-size: 1rem;
}

.project li p {
  margin: 0;
}

.droppable {
  background: #ffe3ee;
}

#finished-projects .droppable {
  background: #d6e1ff;
}

#user-input {
  margin: 1rem;
  padding: 1rem;
  border: 1px solid #ff0062;
  background: #f7f7f7;
}



<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>ProjectManager</title>
    <link rel="stylesheet" href="app.css" />
    <script src="dist/app.js" defer></script>
  </head>
  <body>
    <template id="project-input">
      <form>
        <div class="form-control">
          <label for="title">Title</label>
          <input type="text" id="title" />
        </div>
        <div class="form-control">
          <label for="description">Description</label>
          <textarea id="description" rows="3"></textarea>
        </div>
        <div class="form-control">
          <label for="people">People</label>
          <input type="number" id="people" step="1" min="0" max="10" />
        </div>
        <button type="submit">ADD PROJECT</button>
      </form>
    </template>
    <template id="single-project">
      <li></li>
    </template>
    <template id="project-list">
      <section class="projects">
        <header>
          <h2></h2>
        </header>
        <ul></ul>
      </section>
    </template>
    <div id="app"></div>
  </body>
</html>

index.html文件有一个脚本,dist/app.js从中生成app.ts. 这是app.ts文件:

// autobind decorator
function autobind (_: any, _2: string, descriptor: PropertyDecorator) {
  console.log('start autobind')
  console.log(_, _2, descriptor)
  const originalMethod = descriptor.value
  const adjDescriptor: PropertyDecorator = {
    configurable: true,
    get () {
      const boundFn = originalMethod.bind(this)
      return boundFn
    }
  }
  return adjDescriptor
}

// projectInput Class
class ProjectInput {
  templateElement: HTMLTemplateElement
  hostElement: HTMLDivElement
  element: HTMLElement
  titleInputElement: HTMLInputElement
  descriptionInputElement: HTMLInputElement
  peopleInputElement: HTMLInputElement

  constructor () {
    this.templateElement = document.getElementById(
      'project-input'
    )! as HTMLTemplateElement

    this.hostElement = document.getElementById('app')! as HTMLDivElement

    const importedNode = document.importNode(this.templateElement.content, true)
    this.element = importedNode.firstElementChild as HTMLFormElement
    this.element.id = 'user-input'

    this.titleInputElement = this.element.querySelector(
      '#title'
    ) as HTMLInputElement
    this.descriptionInputElement = this.element.querySelector(
      '#description'
    ) as HTMLInputElement
    this.peopleInputElement = this.element.querySelector(
      '#peole'
    ) as HTMLInputElement

    this.configure()
    this.attach()
  }

  private gatherUserInput (): [string, string, number] | void {
    console.log('start gatherUserInput')

    const enteredTitle = this.titleInputElement.value
    const enteredDescription = this.descriptionInputElement.value
    const enteredPeople = this.peopleInputElement.value

    if (
      enteredTitle.trim().length === 0 ||
      enteredDescription.trim().length === 0 ||
      enteredPeople.trim().length === 0
    ) {
      alert('Invalid input, please try again')
      return
    } else {
      return [enteredTitle, enteredDescription, +enteredPeople]
    }
  }

  @autobind
  private submitHandler (event: Event) {
    event.preventDefault()
    const userInput = this.gatherUserInput()
    if (Array.isArray(userInput)) {
      const [title, desc, people] = userInput
      console.log(title, desc, people)
    }
  }

  private configure () {
    this.element.addEventListener('submit', this.submitHandler.bind(this))
  }

  private attach () {
    this.hostElement.insertAdjacentElement('afterbegin', this.element)
  }
}

const prjInput = new ProjectInput()

这是我得到的在此处输入图像描述

但它无法访问函数gatherUserInput和装饰器autobind,因为我尝试过console.log但没有打印出来。

这是终端记录的内容:

[12:06:19 PM] Starting compilation in watch mode...

src/app.ts:3:37 - error TS2339: Property 'value' does not exist on type 'PropertyDecorator'.

3   const originalMethod = descriptor.value
                                      ~~~~~

src/app.ts:5:5 - error TS2322: Type '{ configurable: boolean; get(): any; }' is not assignable to type 'PropertyDecorator'.
  Object literal may only specify known properties, and 'configurable' does not exist in type 'PropertyDecorator'.

5     configurable: true,
      ~~~~~~~~~~~~~~~~~~

src/app.ts:67:3 - error TS1241: Unable to resolve signature of method decorator when called as an expression.
  Type 'PropertyDecorator' has no properties in common with type 'TypedPropertyDescriptor<(event: Event) => void>'.

67   @autobind
     ~~~~~~~~~

src/app.ts:67:4 - error TS2345: Argument of type 'TypedPropertyDescriptor<(event: Event) => void>' is not assignable to parameter of type 'PropertyDecorator'.
  Type 'TypedPropertyDescriptor<(event: Event) => void>' provides no match for the signature '(target: Object, propertyKey: string | symbol): void'.

67   @autobind
      ~~~~~~~~

[12:06:24 PM] Found 4 errors. Watching for file changes.

请帮我解决这个问题,非常感谢,祝你有美好的一天

标签: javascriptreactjstypescriptwebfrontend

解决方案


快速查看代码后,自动绑定方法似乎没有使用正确的类型。

方法装饰器具有以下参数和参数类型

例如,自动绑定的参数看起来像这样

function 

Autobind(target:any,methodName:string,propertyDescriptor:PropertyDescriptor){
   .....
}

从上面的代码来看,在自动绑定装饰器中使用 PropertyDecarator 代替了 PropertyDescriptor。

如果自动绑定方法编写如下,则代码应该可以正常工作。

   function autobind (_: any, _2: string, descriptor: PropertyDescriptor) {
      const originalMethod = descriptor.value
      const adjDescriptor: PropertyDescriptor = {
        configurable: true,
        get () {
          const boundFn = originalMethod.bind(this)
          return boundFn
        }
      }
      return adjDescriptor
    }

推荐阅读