microsoft/tslint-microsoft-contrib

no-string-based-x rules throw errors in Vue SFC

jwhitmarsh opened this issue · 7 comments

Bug Report

  • tslint-microsoft-contrib version: 6.1.1
  • TSLint version: 5.16.0
  • TypeScript version: 3.4.3
  • Running TSLint via: Vue CLI generated webpack config, which I think uses fork-ts-checker-webpack-plugin

TypeScript code being linted

<template>
  <div class="test">
    {{ msg }}
  </div>
</template>

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

@Component
export default class TestComponent extends Vue {
  msg = 'test'
}
</script>

with tslint.json configuration:

{
  "defaultSeverity": "warning",
  "extends": ["tslint:latest", "tslint-microsoft-contrib/recommended"],
  "linterOptions": {
    "exclude": ["node_modules/**"]
  },
  "rules": {
    "quotemark": [true, "single"],
    "indent": [true, "spaces", 2],
    "interface-name": false,
    "ordered-imports": false,
    "object-literal-sort-keys": false,
    "no-consecutive-blank-lines": false,
    "max-line-length": [
      true,
      {
        "ignore-pattern": "'(.*?)'"
      }
    ],
    "arrow-parens": false,
    "no-default-import": false,
    "match-default-export-name": false,
    "no-implicit-dependencies": [true, ["@"]],
    "completed-docs": false,
    "newline-per-chained-call": false,
    "typedef": false,
    "strict-boolean-expressions": false,
    "no-unsafe-any": false,
    "member-ordering": false,
    "no-default-export": false,
    "no-any": false,
    "no-submodule-imports": false,
    "no-this-assignment": false,
    "member-access": false,
    "trailing-comma": [true, "always"],
    "no-relative-imports": [true, "allow-siblings"],
    "prefer-type-cast": false,
    "import-name": false,
    "export-name": false,
    "prefer-array-literal": false,
    "no-suspicious-comment": false,
    "no-inner-html": false,
    "no-single-line-block-comment": false,
    "no-backbone-get-set-outside-model": false,
  }
}

Actual behavior

The 'no-string-based-set-timeout' rule threw an error in '/Users/jwhitmarsh/src/example/src/Test.vue':
TypeError: Cannot read property 'flags' of undefined
    at tryGetDeclaredTypeOfSymbol (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:36600:24)
    at getDeclaredTypeOfSymbol (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:36597:20)
    at tryGetThisTypeAt (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:46543:99)
    at checkThisExpression (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:46492:24)
    at checkExpressionWorker (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:52215:28)
    at checkExpression (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:52185:42)
    at checkNonNullExpression (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:48560:37)
    at checkPropertyAccessExpressionOrQualifiedName (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:48593:28)
    at checkPropertyAccessExpression (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:48586:20)
    at checkExpressionWorker (/Users/jwhitmarsh/src/example/node_modules/typescript/lib/typescript.js:52242:28)

Expected behavior

Not to throw an error

Indeed. Sadly, this is a duplicate of palantir/tslint#2099 and there's nothing we can really do here 😢

@jwhitmarsh I've tried your example and config with latest @vue/cli and wasn't able to reproduce your error. Only error I've got was missing semicolon. And looks like no-string-based-set-timeout works fine:

$ npm run lint

> tslint-test@0.1.0 lint C:\web\test\vue-tslint-test\tslint-test
> vue-cli-service lint

C:/web/test/vue-tslint-test/tslint-test/src/components/Test.vue
Forbidden setTimeout string parameter: 'console.log("hello");' (no-string-based-set-timeout)
   8 | import { Vue, Component } from 'vue-property-decorator';
   9 | 
> 10 | setTimeout('console.log("hello");', 1000);
  11 | 
  12 | @Component
  13 | export default class TestComponent extends Vue {

Could you try to update typescript or may be @vue/cli.

If you still ahve problem reproduced - could you please create git repo with minimal reproduce case and share link here? Will reopen this issue if will be able to reproduce.

@IllusionMH Good spot, sorry I didn't test a new project, only existing code.

I've replicated it here https://github.com/jwhitmarsh/test-vue. It looks like calling this.$router or this.$data in one of the methods of an SFC causes it, so I don't know if that's because of the $ symbol causing issues with the parser?

EDIT: I should add, that is a recently created new project, so it is using typescript@3.4.5 and @vue/cli@3.7.0.

Thanks for the repro. Was able to see exception in console and will investigate this one.
However I still think that this is most likely External issue.

@jwhitmarsh I've checked this issue and looks like root cause is caused by external factors.
TS isn't able to create symbol for class (when checks this). If I rename file to .ts and leave only content of script tag - rule is able to retrieve type info for class without any problems.
Therefore root cause of this issue should be fixed in intermediate steps that enables TS & TSLint to work with .vue files.

As for this rules - it is only possible to mitigate negative impact of this exception - catch it and use simplified fallback logic. This is the way no-cookies rule currently behaves.

@jwhitmarsh 6.2.0-beta was published to npm. Could you please try install tslint-microsoft-contrib@beta and check if issue is fixed for your actual code.
I've checked with your repro case and it works as expected - uses same logic as when typechecker is not available if encounter exception.

6.2.0 on Wednesday if no blockers.

LGTM! Thanks everyone, really appreciate it!