Stimulus: targets and typescript

Created on 9 Mar 2018  Â·  7Comments  Â·  Source: hotwired/stimulus

Hi there!

Thanks for open sourcing stimulus. It's been a real joy to use so far and I'm excited to really dig deep into it.

I noticed that the default example creates a typescript compiler error.

import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "output" ]

  connect() {
    this.outputTarget.textContent = 'Hello, Stimulus!' //[ts] Property 'outputTarget' does not exist on type 'default'.
  }
}

The Stimulus documentation says:

When Stimulus loads your controller class, it looks for target name strings in a static array called targets. For each target name in the array, Stimulus adds three new properties to your controller. Here, our "source" target name becomes the following properties:

  • this.sourceTarget evaluates to the first source target in your controller’s scope. If there is no source target, accessing the property throws an error.
  • this.sourceTargets evaluates to an array of all source targets in the controller’s scope.
  • this.hasSourceTarget evaluates to true if there is a source target or false if not.

Is there anyway to let Typescript know about this.sourceTarget, this.sourceTargets, and this.hasSourceTarget? I thought this might be alleviate since the library is written in Typescript.

All 7 comments

According to https://discourse.stimulusjs.org/t/syntax-error-when-using-targets/23/8 this is not (yet?) possible, instead you’d have to declare each target explicitly.

Thank you @docstun! I see that someone has the same problem as me. I should have checked the forum too :). Since the maintainers are aware of the issue and there is a workaround, I'll close this issue.

Workaround for future searchers:
````
import { Controller } from "stimulus"

export default class extends Controller {
nameTarget: Element
nameTargets: Element[]
hasNameTarget: boolean

static targets = [ "name" ]

// …
}
```

I'm not sure how easy/hard this would be to do, but it looks like a similar problem to defaultProps for JSX. That was just added in TS 3.0.

FYI:

If you use @sunnyrjuneja 's sulotion, you will need this config in tsconfig file, otherwise it will raise an error: xxxx has no initializer and is not definitely assigned in the constructor.

"strictPropertyInitialization":false

@NaiveCAI This is my tsconfig:

{
  "compilerOptions": {
    "declaration": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "lib": ["es6", "dom"],
    "module": "es6",
    "moduleResolution": "node",
    "sourceMap": true,
    "target": "es5"
  },
  "exclude": [
    "**/*.spec.ts",
    "node_modules",
    "vendor",
    "public"
  ],
  "compileOnSave": false
}

FYI:

If you use @sunnyrjuneja 's sulotion, you will need this config in tsconfig file, otherwise it will raise an error: xxxx has no initializer and is not definitely assigned in the constructor.

"strictPropertyInitialization":false

You can use an exclamation point to bypass the strict initialization check:

class extends Controller {
  static targets = [ "name" ]

  readonly nameTarget!: Element
  readonly nameTargets!: Element[]
  readonly hasNameTargets!: boolean
}

I've been trying to use the approach listed here but I've come across an issue.

Using syntax such as

class extends Controller {
  static targets = [ "name" ]

  readonly nameTarget!: Element
  readonly nameTargets!: Element[]
  readonly hasNameTargets!: boolean
}

This gets compiled down to:

_this.nameTarget = void 0;
_this.nameTargets = void 0;
_this.hasNameTarget = void 0;

And an error appears in the console:

TypeError: Cannot set property nameTarget of #<Controller> which has only a getter

Pausing the debugger at this point, inspecting this.nameTarget reveals that it has already been set to the correct DOM element, and stimulus is (correctly) stopping any assignment to this value.

I wonder if there is a way to tell TypeScript that we have a nameTarget property without compiling this into code that tries to initialise the property? (I do wonder if this is a babel issue rather than TypeScript though).

EDIT: on further investigation it appears that this is the behaviour of the @babel/plugin-proposal-class-properties plugin, if you do not initialise the property then it will compile down to initialise with void 0. I guess I need to find another way to make TypeScript happy 😄

Related: https://github.com/babel/babel/issues/7644

I'll open a new issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

savroff picture savroff  Â·  4Comments

tiagocassio picture tiagocassio  Â·  3Comments

gerdriesselmann picture gerdriesselmann  Â·  3Comments

saneef picture saneef  Â·  4Comments

JanaDeppe picture JanaDeppe  Â·  4Comments