Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

targets and typescript #121

Closed
sunnyrjuneja opened this issue Mar 9, 2018 · 7 comments
Closed

targets and typescript #121

sunnyrjuneja opened this issue Mar 9, 2018 · 7 comments

Comments

@sunnyrjuneja
Copy link
Contributor

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.

@docstun
Copy link

docstun commented Mar 11, 2018

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.

@sunnyrjuneja
Copy link
Contributor Author

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" ]

  // …
}
```

@danielcompton
Copy link

danielcompton commented Jul 30, 2018

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.

@NaiveCAI
Copy link

NaiveCAI commented Nov 6, 2018

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

@sunnyrjuneja
Copy link
Contributor Author

@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
}

@sstephenson
Copy link
Contributor

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
}

@fiznool
Copy link

fiznool commented Jan 17, 2019

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: babel/babel#7644

I'll open a new issue.

mariochavez added a commit to michelada/ektar that referenced this issue Apr 7, 2020
The application will work with Typescript by default.
Be aware of how it works with Stimulusjs

hotwired/stimulus#121
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

6 participants