diff --git a/package.json b/package.json index fe019d1..7fae1df 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "gulp-sourcemaps": "^1.6.0", "gulp-tag-version": "^1.3.0", "lazypipe": "^1.0.1", - "vinyl-source-stream": "^1.1.0" + "vinyl-source-stream": "^1.1.0", + "typescript-eslint-parser": "^1.0.0" } -} +} \ No newline at end of file diff --git a/src/referencer.js b/src/referencer.js index bd81080..bb99130 100644 --- a/src/referencer.js +++ b/src/referencer.js @@ -222,11 +222,15 @@ export default class Referencer extends esrecurse.Visitor { }); } - // Skip BlockStatement to prevent creating BlockStatement scope. - if (node.body.type === Syntax.BlockStatement) { - this.visitChildren(node.body); - } else { - this.visit(node.body); + // In TypeScript there are a number of function-like constructs which have no body, + // so check it exists before traversing + if (node.body) { + // Skip BlockStatement to prevent creating BlockStatement scope. + if (node.body.type === Syntax.BlockStatement) { + this.visitChildren(node.body); + } else { + this.visit(node.body); + } } this.close(node); diff --git a/src/scope.js b/src/scope.js index 0e4d8c2..38ec0f0 100644 --- a/src/scope.js +++ b/src/scope.js @@ -61,6 +61,10 @@ function isStrictScope(scope, block, isMethodDefinition, useDirective) { } else { body = block.body; } + + if (!body) { + return false; + } } else if (scope.type === 'global') { body = block; } else { diff --git a/test/typescript.js b/test/typescript.js new file mode 100644 index 0000000..866a010 --- /dev/null +++ b/test/typescript.js @@ -0,0 +1,67 @@ +/** + * @fileoverview Typescript scope tests + * @author Reyad Attiyat + */ +"use strict"; + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +require("babel-register"); + +const expect = require('chai').expect, + parse = require('typescript-eslint-parser').parse, + analyze = require('../src').analyze; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +describe('typescript', () => { + describe('multiple call signatures', () => { + it('should create a function scope', () => { + const ast = parse(` + function foo(bar: number): number; + function foo(bar: string): string; + function foo(bar: string | number): string | number { + return bar; + } + `); + + const scopeManager = analyze(ast); + + expect(scopeManager.scopes).to.have.length(4); + + const globalScope = scopeManager.scopes[0]; + expect(globalScope.type).to.be.equal('global'); + expect(globalScope.variables).to.have.length(1); + expect(globalScope.references).to.have.length(0); + expect(globalScope.isArgumentsMaterialized()).to.be.true; + + // Function scopes + let scope = scopeManager.scopes[1]; + expect(scope.type).to.be.equal('function'); + expect(scope.variables).to.have.length(2); + expect(scope.variables[0].name).to.be.equal('arguments'); + expect(scope.isArgumentsMaterialized()).to.be.false; + expect(scope.references).to.have.length(0); + + scope = scopeManager.scopes[2]; + expect(scope.type).to.be.equal('function'); + expect(scope.variables).to.have.length(2); + expect(scope.variables[0].name).to.be.equal('arguments'); + expect(scope.isArgumentsMaterialized()).to.be.false; + expect(scope.references).to.have.length(0); + + scope = scopeManager.scopes[3]; + expect(scope.type).to.be.equal('function'); + expect(scope.variables).to.have.length(2); + expect(scope.variables[0].name).to.be.equal('arguments'); + expect(scope.isArgumentsMaterialized()).to.be.false; + expect(scope.references).to.have.length(1); + + + }); + }); +});