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.
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.