0

I have a Vue component which makes use of vue-boostrap Modal component, with a definition that looks something like this:

<template>
    <div>
        <b-modal ref="NewProjectDialog" id="NewProjectDialog" centered>
        </b-modal>
    </div>
</template>

<script lang="ts">
    import { Component, Vue, Prop } from 'vue-property-decorator'
    import {BModal} from 'bootstrap-vue'

    @Component()
    export default class Projects extends Vue {
        save_project() {
            (this.$refs['NewProjectDialog'] as BModal).hide()
        }
    }

The above code works correctly, however there appears to be a mistake in the way I have cast $ref['NewProjectDialog'], because it appears that the compiler picks up the preceding line as part of the expression, e.g. the following code generates a "Cannot invoke an expression whose type lacks a call signature." error:

save_project() {
    console.log('any code line without a semicolon fails')
    (this.$refs['NewProjectDialog'] as BModal).hide()
}

but the following code compiles fine:

save_project() {
    console.log('any code line without a semicolon fails');
    (this.$refs['NewProjectDialog'] as BModal).hide()
}

Can anyone explain why I need the line separator for the code to work correctly?

Ian Ash
  • 1,087
  • 11
  • 23
  • 1
    It's because ambiguous cases break in the absence of a semicolon. In short, you should always use a semicolon at the end of a statement. – tao Jun 17 '19 at 22:02
  • 1
    Possible duplicate of [Do you recommend using semicolons after every statement in JavaScript?](https://stackoverflow.com/questions/444080/do-you-recommend-using-semicolons-after-every-statement-in-javascript) – tao Jun 17 '19 at 22:02
  • @AndreiGheorghiu I'm not sure, the question being asked is very different. The second most upvoted answer covers a similar case, but not so identical to be immediately obviously the same to an untrained eye. I guess I see this question as asking why the answers to that question apply to this code specifically. – rh16 Jun 17 '19 at 22:10
  • The answer you accepted is the same as the possible duplicate. `(an expression)` following an unterminated statement line will attempt to read the result of the unterminated statement as a function and call it with the result of `an expression` as parameter. But allow me to make it clearer: terminating each statement in JavaScript (or TypeScript) with a semicolon will have 2 immediate effects: a) it will make you a much better and reliable programmer (making you earn more/hour) and b) will also make you appear to others like a much better and reliable programmer (making you earn more/hour). – tao Jun 17 '19 at 22:21

2 Answers2

2

The semicolon is needed here to remove ambiguity in the syntax.

The problem is not specifically that you are doing a cast, but that the cast needs to be wrapped in brackets.

It's probably easier to understand if we strip away the other parts of the code and just look at the syntax:

foo()
(bar).fizz()
// vs
foo();
(bar).fixx

Looks the same, but to the interpreter, the first one looks like

foo()(bar).fizz()

i.e you're calling the result of foo() as a function.

If we define our variables differently this syntax make a lot of sense:

function foo() {
    return a => a*2;
}
var bar = 2;

foo()
(bar).toFixed(2);
// Prints 4.00

Adding the semicolon tells the interpreter that foo() is a seperate call and the next line is not operating on its result as a function.

rh16
  • 1,056
  • 14
  • 24
0

BootstrapVue v2.0.0-rc.21 (and newer) has a simpler method for showing and hiding modals:

this.$bvModal.hide('id-of-modal')
this.$bvModal.show('id-of-modal')

The above this.$bvModal has type declarations, so it should work without any casting.

Troy Morehouse
  • 5,320
  • 1
  • 27
  • 38