diff --git a/.travis.yml b/.travis.yml index 478e31c4398..bfc07e2b510 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,12 +7,18 @@ node_js: sudo: false +env: + - workerCount=3 + matrix: fast_finish: true include: - os: osx node_js: stable osx_image: xcode7.3 + env: workerCount=2 + allow_failures: + - os: osx branches: only: @@ -28,3 +34,6 @@ install: cache: directories: - node_modules + +git: + depth: 1 diff --git a/Gulpfile.ts b/Gulpfile.ts index 6c91ef52cbb..2718669b1f7 100644 --- a/Gulpfile.ts +++ b/Gulpfile.ts @@ -34,7 +34,7 @@ import through2 = require("through2"); import merge2 = require("merge2"); import intoStream = require("into-stream"); import * as os from "os"; -import Linter = require("tslint"); +import fold = require("travis-fold"); const gulp = helpMaker(originalGulp); const mochaParallel = require("./scripts/mocha-parallel.js"); const {runTestsInParallel} = mochaParallel; @@ -449,7 +449,7 @@ gulp.task(tsserverLibraryFile, false, [servicesFile], (done) => { }); gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile]); -gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON]); +gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile]); gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]); @@ -503,7 +503,7 @@ gulp.task("VerifyLKG", false, [], () => { return gulp.src(expectedFiles).pipe(gulp.dest(LKGDirectory)); }); -gulp.task("LKGInternal", false, ["lib", "local", "lssl"]); +gulp.task("LKGInternal", false, ["lib", "local"]); gulp.task("LKG", "Makes a new LKG out of the built js files", ["clean", "dontUseDebugMode"], () => { return runSequence("LKGInternal", "VerifyLKG"); @@ -928,26 +928,6 @@ gulp.task("build-rules", "Compiles tslint rules to js", () => { .pipe(gulp.dest(dest)); }); -function getLinterOptions() { - return { - configuration: require("./tslint.json"), - formatter: "prose", - formattersDirectory: undefined, - rulesDirectory: "built/local/tslint" - }; -} - -function lintFileContents(options, path, contents) { - const ll = new Linter(path, contents, options); - console.log("Linting '" + path + "'."); - return ll.lint(); -} - -function lintFile(options, path) { - const contents = fs.readFileSync(path, "utf8"); - return lintFileContents(options, path, contents); -} - const lintTargets = [ "Gulpfile.ts", "src/compiler/**/*.ts", @@ -959,27 +939,72 @@ const lintTargets = [ "tests/*.ts", "tests/webhost/*.ts" // Note: does *not* descend recursively ]; +function sendNextFile(files: {path: string}[], child: cp.ChildProcess, callback: (failures: number) => void, failures: number) { + const file = files.pop(); + if (file) { + console.log(`Linting '${file.path}'.`); + child.send({ kind: "file", name: file.path }); + } + else { + child.send({ kind: "close" }); + callback(failures); + } +} + +function spawnLintWorker(files: {path: string}[], callback: (failures: number) => void) { + const child = cp.fork("./scripts/parallel-lint"); + let failures = 0; + child.on("message", function(data) { + switch (data.kind) { + case "result": + if (data.failures > 0) { + failures += data.failures; + console.log(data.output); + } + sendNextFile(files, child, callback, failures); + break; + case "error": + console.error(data.error); + failures++; + sendNextFile(files, child, callback, failures); + break; + } + }); + sendNextFile(files, child, callback, failures); +} gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex", ["build-rules"], () => { const fileMatcher = RegExp(cmdLineOptions["files"]); - const lintOptions = getLinterOptions(); - let failed = 0; - return gulp.src(lintTargets) - .pipe(insert.transform((contents, file) => { - if (!fileMatcher.test(file.path)) return contents; - const result = lintFile(lintOptions, file.path); - if (result.failureCount > 0) { - console.log(result.output); - failed += result.failureCount; + if (fold.isTravis()) console.log(fold.start("lint")); + + let files: {stat: fs.Stats, path: string}[] = []; + return gulp.src(lintTargets, { read: false }) + .pipe(through2.obj((chunk, enc, cb) => { + files.push(chunk); + cb(); + }, (cb) => { + files = files.filter(file => fileMatcher.test(file.path)).sort((filea, fileb) => filea.stat.size - fileb.stat.size); + const workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length; + for (let i = 0; i < workerCount; i++) { + spawnLintWorker(files, finished); } - return contents; // TODO (weswig): Automatically apply fixes? :3 - })) - .on("end", () => { - if (failed > 0) { - console.error("Linter errors."); - process.exit(1); + + let completed = 0; + let failures = 0; + function finished(fails) { + completed++; + failures += fails; + if (completed === workerCount) { + if (fold.isTravis()) console.log(fold.end("lint")); + if (failures > 0) { + throw new Error(`Linter errors: ${failures}`); + } + else { + cb(); + } + } } - }); + })); }); diff --git a/Jakefile.js b/Jakefile.js index 174be5e702f..e4aaf330dc7 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -4,7 +4,7 @@ var fs = require("fs"); var os = require("os"); var path = require("path"); var child_process = require("child_process"); -var Linter = require("tslint"); +var fold = require("travis-fold"); var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel; // Variables @@ -32,6 +32,28 @@ if (process.env.path !== undefined) { process.env.PATH = nodeModulesPathPrefix + process.env.PATH; } +function toNs(diff) { + return diff[0] * 1e9 + diff[1]; +} + +function mark() { + if (!fold.isTravis()) return; + var stamp = process.hrtime(); + var id = Math.floor(Math.random() * 0xFFFFFFFF).toString(16); + console.log("travis_time:start:" + id + "\r"); + return { + stamp: stamp, + id: id + }; +} + +function measure(marker) { + if (!fold.isTravis()) return; + var diff = process.hrtime(marker.stamp); + var total = [marker.stamp[0] + diff[0], marker.stamp[1] + diff[1]]; + console.log("travis_time:end:" + marker.id + ":start=" + toNs(marker.stamp) + ",finish=" + toNs(total) + ",duration=" + toNs(diff) + "\r"); +} + var compilerSources = [ "core.ts", "performance.ts", @@ -285,6 +307,7 @@ var builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename); */ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) { file(outFile, prereqs, function() { + var startCompileTime = mark(); opts = opts || {}; var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler; var options = "--noImplicitAny --noImplicitThis --noEmitOnError --types " @@ -361,11 +384,13 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts callback(); } + measure(startCompileTime); complete(); }); ex.addListener("error", function() { fs.unlinkSync(outFile); fail("Compilation of " + outFile + " unsuccessful"); + measure(startCompileTime); }); ex.run(); }, {async: true}); @@ -551,7 +576,7 @@ var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibr compileFile( tsserverLibraryFile, languageServiceLibrarySources, - [builtLocalDirectory, copyright].concat(languageServiceLibrarySources), + [builtLocalDirectory, copyright, builtLocalCompiler].concat(languageServiceLibrarySources).concat(libraryTargets), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true, { noOutFile: false, generateDeclarations: true }); @@ -560,9 +585,19 @@ compileFile( desc("Builds language service server library"); task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]); +desc("Emit the start of the build fold"); +task("build-fold-start", [] , function() { + if (fold.isTravis()) console.log(fold.start("build")); +}); + +desc("Emit the end of the build fold"); +task("build-fold-end", [] , function() { + if (fold.isTravis()) console.log(fold.end("build")); +}); + // Local target to build the compiler and services desc("Builds the full compiler and services"); -task("local", ["generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON]); +task("local", ["build-fold-start", "generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON, "lssl", "build-fold-end"]); // Local target to build only tsc.js desc("Builds only the compiler"); @@ -617,7 +652,7 @@ task("generate-spec", [specMd]); // Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory desc("Makes a new LKG out of the built js files"); -task("LKG", ["clean", "release", "local", "lssl"].concat(libraryTargets), function() { +task("LKG", ["clean", "release", "local"].concat(libraryTargets), function() { var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets); var missingFiles = expectedFiles.filter(function (f) { return !fs.existsSync(f); @@ -758,6 +793,7 @@ function runConsoleTests(defaultReporter, runInParallel) { // timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally // default timeout is 2sec which really should be enough, but maybe we just need a small amount longer if(!runInParallel) { + var startTime = mark(); tests = tests ? ' -g "' + tests + '"' : ''; var cmd = "mocha" + (debug ? " --debug-brk" : "") + " -R " + reporter + tests + colors + bail + ' -t ' + testTimeout + ' ' + run; console.log(cmd); @@ -766,10 +802,12 @@ function runConsoleTests(defaultReporter, runInParallel) { process.env.NODE_ENV = "development"; exec(cmd, function () { process.env.NODE_ENV = savedNodeEnv; + measure(startTime); runLinter(); finish(); }, function(e, status) { process.env.NODE_ENV = savedNodeEnv; + measure(startTime); finish(status); }); @@ -777,9 +815,10 @@ function runConsoleTests(defaultReporter, runInParallel) { else { var savedNodeEnv = process.env.NODE_ENV; process.env.NODE_ENV = "development"; + var startTime = mark(); runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) { process.env.NODE_ENV = savedNodeEnv; - + measure(startTime); // last worker clean everything and runs linter in case if there were no errors deleteTemporaryProjectOutput(); jake.rmRf(taskConfigsFolder); @@ -998,41 +1037,21 @@ var tslintRulesOutFiles = tslintRules.map(function(p) { return path.join(builtLocalDirectory, "tslint", p + ".js"); }); desc("Compiles tslint rules to js"); -task("build-rules", tslintRulesOutFiles); +task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(["build-rules-end"])); tslintRulesFiles.forEach(function(ruleFile, i) { compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false, { noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint")}); }); -function getLinterOptions() { - return { - configuration: require("./tslint.json"), - formatter: "prose", - formattersDirectory: undefined, - rulesDirectory: "built/local/tslint" - }; -} +desc("Emit the start of the build-rules fold"); +task("build-rules-start", [] , function() { + if (fold.isTravis()) console.log(fold.start("build-rules")); +}); -function lintFileContents(options, path, contents) { - var ll = new Linter(path, contents, options); - console.log("Linting '" + path + "'."); - return ll.lint(); -} - -function lintFile(options, path) { - var contents = fs.readFileSync(path, "utf8"); - return lintFileContents(options, path, contents); -} - -function lintFileAsync(options, path, cb) { - fs.readFile(path, "utf8", function(err, contents) { - if (err) { - return cb(err); - } - var result = lintFileContents(options, path, contents); - cb(undefined, result); - }); -} +desc("Emit the end of the build-rules fold"); +task("build-rules-end", [] , function() { + if (fold.isTravis()) console.log(fold.end("build-rules")); +}); var lintTargets = compilerSources .concat(harnessSources) @@ -1044,71 +1063,78 @@ var lintTargets = compilerSources .concat(["Gulpfile.ts"]) .concat([nodeServerInFile, perftscPath, "tests/perfsys.ts", webhostPath]); +function sendNextFile(files, child, callback, failures) { + var file = files.pop(); + if (file) { + console.log("Linting '" + file + "'."); + child.send({kind: "file", name: file}); + } + else { + child.send({kind: "close"}); + callback(failures); + } +} + +function spawnLintWorker(files, callback) { + var child = child_process.fork("./scripts/parallel-lint"); + var failures = 0; + child.on("message", function(data) { + switch (data.kind) { + case "result": + if (data.failures > 0) { + failures += data.failures; + console.log(data.output); + } + sendNextFile(files, child, callback, failures); + break; + case "error": + console.error(data.error); + failures++; + sendNextFile(files, child, callback, failures); + break; + } + }); + sendNextFile(files, child, callback, failures); +} desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex"); task("lint", ["build-rules"], function() { - var lintOptions = getLinterOptions(); + if (fold.isTravis()) console.log(fold.start("lint")); + var startTime = mark(); var failed = 0; var fileMatcher = RegExp(process.env.f || process.env.file || process.env.files || ""); var done = {}; for (var i in lintTargets) { var target = lintTargets[i]; if (!done[target] && fileMatcher.test(target)) { - var result = lintFile(lintOptions, target); - if (result.failureCount > 0) { - console.log(result.output); - failed += result.failureCount; - } - done[target] = true; + done[target] = fs.statSync(target).size; } } - if (failed > 0) { - fail('Linter errors.', failed); - } -}); -/** - * This is required because file watches on Windows get fires _twice_ - * when a file changes on some node/windows version configuations - * (node v4 and win 10, for example). By not running a lint for a file - * which already has a pending lint, we avoid duplicating our work. - * (And avoid printing duplicate results!) - */ -var lintSemaphores = {}; + var workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length; -function lintWatchFile(filename) { - fs.watch(filename, {persistent: true}, function(event) { - if (event !== "change") { - return; - } - - if (!lintSemaphores[filename]) { - lintSemaphores[filename] = true; - lintFileAsync(getLinterOptions(), filename, function(err, result) { - delete lintSemaphores[filename]; - if (err) { - console.log(err); - return; - } - if (result.failureCount > 0) { - console.log("***Lint failure***"); - for (var i = 0; i < result.failures.length; i++) { - var failure = result.failures[i]; - var start = failure.startPosition.lineAndCharacter; - var end = failure.endPosition.lineAndCharacter; - console.log("warning " + filename + " (" + (start.line + 1) + "," + (start.character + 1) + "," + (end.line + 1) + "," + (end.character + 1) + "): " + failure.failure); - } - console.log("*** Total " + result.failureCount + " failures."); - } - }); - } + var names = Object.keys(done).sort(function(namea, nameb) { + return done[namea] - done[nameb]; }); -} -desc("Watches files for changes to rerun a lint pass"); -task("lint-server", ["build-rules"], function() { - console.log("Watching ./src for changes to linted files"); - for (var i = 0; i < lintTargets.length; i++) { - lintWatchFile(lintTargets[i]); + for (var i = 0; i < workerCount; i++) { + spawnLintWorker(names, finished); } -}); + + var completed = 0; + var failures = 0; + function finished(fails) { + completed++; + failures += fails; + if (completed === workerCount) { + measure(startTime); + if (fold.isTravis()) console.log(fold.end("lint")); + if (failures > 0) { + fail('Linter errors.', failed); + } + else { + complete(); + } + } + } +}, {async: true}); diff --git a/README.md b/README.md index fca2890bc77..d16bc363b26 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Join the chat at https://gitter.im/Microsoft/TypeScript](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Microsoft/TypeScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[TypeScript](http://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types, classes, and modules to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](http://www.typescriptlang.org/Playground), and stay up to date via [our blog](http://blogs.msdn.com/typescript) and [Twitter account](https://twitter.com/typescriptlang). +[TypeScript](http://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types, classes, and modules to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](http://www.typescriptlang.org/Playground), and stay up to date via [our blog](https://blogs.msdn.microsoft.com/typescript) and [Twitter account](https://twitter.com/typescriptlang). ## Installing diff --git a/package.json b/package.json index 5606a423a0e..25d8f0c42ea 100644 --- a/package.json +++ b/package.json @@ -30,8 +30,8 @@ }, "devDependencies": { "@types/browserify": "latest", - "@types/convert-source-map": "latest", "@types/chai": "latest", + "@types/convert-source-map": "latest", "@types/del": "latest", "@types/glob": "latest", "@types/gulp": "latest", @@ -72,13 +72,14 @@ "run-sequence": "latest", "sorcery": "latest", "through2": "latest", + "travis-fold": "latest", "ts-node": "latest", "tslint": "next", "typescript": "next" }, "scripts": { "pretest": "jake tests", - "test": "jake runtests", + "test": "jake runtests-parallel", "build": "npm run build:compiler && npm run build:tests", "build:compiler": "jake local", "build:tests": "jake tests", diff --git a/scripts/parallel-lint.js b/scripts/parallel-lint.js new file mode 100644 index 00000000000..a9aec06c2df --- /dev/null +++ b/scripts/parallel-lint.js @@ -0,0 +1,45 @@ +var Linter = require("tslint"); +var fs = require("fs"); + +function getLinterOptions() { + return { + configuration: require("../tslint.json"), + formatter: "prose", + formattersDirectory: undefined, + rulesDirectory: "built/local/tslint" + }; +} + +function lintFileContents(options, path, contents) { + var ll = new Linter(path, contents, options); + return ll.lint(); +} + +function lintFileAsync(options, path, cb) { + fs.readFile(path, "utf8", function (err, contents) { + if (err) { + return cb(err); + } + var result = lintFileContents(options, path, contents); + cb(undefined, result); + }); +} + +process.on("message", function (data) { + switch (data.kind) { + case "file": + var target = data.name; + var lintOptions = getLinterOptions(); + lintFileAsync(lintOptions, target, function (err, result) { + if (err) { + process.send({ kind: "error", error: err.toString() }); + return; + } + process.send({ kind: "result", failures: result.failureCount, output: result.output }); + }); + break; + case "close": + process.exit(0); + break; + } +}); \ No newline at end of file diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts index 26632ba6bab..431cf460180 100644 --- a/scripts/processDiagnosticMessages.ts +++ b/scripts/processDiagnosticMessages.ts @@ -69,7 +69,7 @@ function checkForUniqueCodes(messages: string[], diagnosticTable: InputDiagnosti } function buildUniqueNameMap(names: string[]): ts.Map { - var nameMap: ts.Map = {}; + var nameMap = ts.createMap(); var uniqueNames = NameGenerator.ensureUniqueness(names, /* isCaseSensitive */ false, /* isFixed */ undefined); diff --git a/scripts/tslint/preferConstRule.ts b/scripts/tslint/preferConstRule.ts index aaa1b0e53d5..1d316692468 100644 --- a/scripts/tslint/preferConstRule.ts +++ b/scripts/tslint/preferConstRule.ts @@ -1,7 +1,6 @@ import * as Lint from "tslint/lib/lint"; import * as ts from "typescript"; - export class Rule extends Lint.Rules.AbstractRule { public static FAILURE_STRING_FACTORY = (identifier: string) => `Identifier '${identifier}' never appears on the LHS of an assignment - use const instead of let for its declaration.`; @@ -64,7 +63,7 @@ interface DeclarationUsages { } class PreferConstWalker extends Lint.RuleWalker { - private inScopeLetDeclarations: ts.Map[] = []; + private inScopeLetDeclarations: ts.MapLike[] = []; private errors: Lint.RuleFailure[] = []; private markAssignment(identifier: ts.Identifier) { const name = identifier.text; @@ -172,7 +171,7 @@ class PreferConstWalker extends Lint.RuleWalker { } private visitAnyForStatement(node: ts.ForOfStatement | ts.ForInStatement) { - const names: ts.Map = {}; + const names: ts.MapLike = {}; if (isLet(node.initializer)) { if (node.initializer.kind === ts.SyntaxKind.VariableDeclarationList) { this.collectLetIdentifiers(node.initializer as ts.VariableDeclarationList, names); @@ -194,7 +193,7 @@ class PreferConstWalker extends Lint.RuleWalker { } visitBlock(node: ts.Block) { - const names: ts.Map = {}; + const names: ts.MapLike = {}; for (const statement of node.statements) { if (statement.kind === ts.SyntaxKind.VariableStatement) { this.collectLetIdentifiers((statement as ts.VariableStatement).declarationList, names); @@ -205,7 +204,7 @@ class PreferConstWalker extends Lint.RuleWalker { this.popDeclarations(); } - private collectLetIdentifiers(list: ts.VariableDeclarationList, ret: ts.Map) { + private collectLetIdentifiers(list: ts.VariableDeclarationList, ret: ts.MapLike) { for (const node of list.declarations) { if (isLet(node) && !isExported(node)) { this.collectNameIdentifiers(node, node.name, ret); @@ -213,7 +212,7 @@ class PreferConstWalker extends Lint.RuleWalker { } } - private collectNameIdentifiers(declaration: ts.VariableDeclaration, node: ts.Identifier | ts.BindingPattern, table: ts.Map) { + private collectNameIdentifiers(declaration: ts.VariableDeclaration, node: ts.Identifier | ts.BindingPattern, table: ts.MapLike) { if (node.kind === ts.SyntaxKind.Identifier) { table[(node as ts.Identifier).text] = { declaration, usages: 0 }; } @@ -222,7 +221,7 @@ class PreferConstWalker extends Lint.RuleWalker { } } - private collectBindingPatternIdentifiers(value: ts.VariableDeclaration, pattern: ts.BindingPattern, table: ts.Map) { + private collectBindingPatternIdentifiers(value: ts.VariableDeclaration, pattern: ts.BindingPattern, table: ts.MapLike) { for (const element of pattern.elements) { this.collectNameIdentifiers(value, element.name, table); } diff --git a/scripts/types/ambient.d.ts b/scripts/types/ambient.d.ts index e77e3fe8c5a..e83489801d7 100644 --- a/scripts/types/ambient.d.ts +++ b/scripts/types/ambient.d.ts @@ -22,3 +22,4 @@ declare module "into-stream" { } declare module "sorcery"; +declare module "travis-fold"; diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 849f503ef85..ee82a88d7a5 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -89,9 +89,10 @@ namespace ts { const binder = createBinder(); export function bindSourceFile(file: SourceFile, options: CompilerOptions) { - const start = performance.mark(); + performance.mark("beforeBind"); binder(file, options); - performance.measure("Bind", start); + performance.mark("afterBind"); + performance.measure("Bind", "beforeBind", "afterBind"); } function createBinder(): (file: SourceFile, options: CompilerOptions) => void { @@ -135,7 +136,7 @@ namespace ts { options = opts; languageVersion = getEmitScriptTarget(options); inStrictMode = !!file.externalModuleIndicator; - classifiableNames = {}; + classifiableNames = createMap(); symbolCount = 0; Symbol = objectAllocator.getSymbolConstructor(); @@ -183,11 +184,11 @@ namespace ts { symbol.declarations.push(node); if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) { - symbol.exports = {}; + symbol.exports = createMap(); } if (symbolFlags & SymbolFlags.HasMembers && !symbol.members) { - symbol.members = {}; + symbol.members = createMap(); } if (symbolFlags & SymbolFlags.Value) { @@ -325,9 +326,7 @@ namespace ts { // Otherwise, we'll be merging into a compatible existing symbol (for example when // you have multiple 'vars' with the same name in the same container). In this case // just add this node into the declarations list of the symbol. - symbol = hasProperty(symbolTable, name) - ? symbolTable[name] - : (symbolTable[name] = createSymbol(SymbolFlags.None, name)); + symbol = symbolTable[name] || (symbolTable[name] = createSymbol(SymbolFlags.None, name)); if (name && (includes & SymbolFlags.Classifiable)) { classifiableNames[name] = name; @@ -445,7 +444,7 @@ namespace ts { if (containerFlags & ContainerFlags.IsContainer) { container = blockScopeContainer = node; if (containerFlags & ContainerFlags.HasLocals) { - container.locals = {}; + container.locals = createMap(); } addToContainerChain(container); } @@ -1410,7 +1409,8 @@ namespace ts { const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type"); addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral); - typeLiteralSymbol.members = { [symbol.name]: symbol }; + typeLiteralSymbol.members = createMap(); + typeLiteralSymbol.members[symbol.name] = symbol; } function bindObjectLiteralExpression(node: ObjectLiteralExpression) { @@ -1420,7 +1420,7 @@ namespace ts { } if (inStrictMode) { - const seen: Map = {}; + const seen = createMap(); for (const prop of node.properties) { if (prop.name.kind !== SyntaxKind.Identifier) { @@ -1476,7 +1476,7 @@ namespace ts { // fall through. default: if (!blockScopeContainer.locals) { - blockScopeContainer.locals = {}; + blockScopeContainer.locals = createMap(); addToContainerChain(blockScopeContainer); } declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes); @@ -1898,18 +1898,17 @@ namespace ts { } function bindExportAssignment(node: ExportAssignment | BinaryExpression) { - const boundExpression = node.kind === SyntaxKind.ExportAssignment ? (node).expression : (node).right; if (!container.symbol || !container.symbol.exports) { // Export assignment in some sort of block construct bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node)); } - else if (boundExpression.kind === SyntaxKind.Identifier && node.kind === SyntaxKind.ExportAssignment) { - // An export default clause with an identifier exports all meanings of that identifier - declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Alias, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); - } else { - // An export default clause with an expression exports a value - declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); + const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node) + // An export default clause with an EntityNameExpression exports all meanings of that identifier + ? SymbolFlags.Alias + // An export default clause with any other expression exports a value + : SymbolFlags.Property; + declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes); } } @@ -1936,7 +1935,7 @@ namespace ts { } } - file.symbol.globalExports = file.symbol.globalExports || {}; + file.symbol.globalExports = file.symbol.globalExports || createMap(); declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes); } @@ -1984,12 +1983,23 @@ namespace ts { assignee = container; } else if (container.kind === SyntaxKind.Constructor) { - assignee = container.parent; + if (isInJavaScriptFile(node)) { + // this.foo assignment in a JavaScript class + // Bind this property to the containing class + const saveContainer = container; + container = container.parent; + bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None); + container = saveContainer; + return; + } + else { + assignee = container.parent; + } } else { return; } - assignee.symbol.members = assignee.symbol.members || {}; + assignee.symbol.members = assignee.symbol.members || createMap(); // It's acceptable for multiple 'this' assignments of the same identifier to occur // AND it can be overwritten by subsequent method declarations const symbol = declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property); @@ -2017,7 +2027,7 @@ namespace ts { // Set up the members collection if it doesn't exist already if (!funcSymbol.members) { - funcSymbol.members = {}; + funcSymbol.members = createMap(); } // Declare the method/property @@ -2066,7 +2076,7 @@ namespace ts { // module might have an exported variable called 'prototype'. We can't allow that as // that would clash with the built-in 'prototype' for the class. const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype"); - if (hasProperty(symbol.exports, prototypeSymbol.name)) { + if (symbol.exports[prototypeSymbol.name]) { if (node.name) { node.name.parent = node; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c1626ff0b75..3d7fb7eb7e1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -44,7 +44,7 @@ namespace ts { let symbolCount = 0; const emptyArray: any[] = []; - const emptySymbols: SymbolTable = {}; + const emptySymbols = createMap(); const compilerOptions = host.getCompilerOptions(); const languageVersion = compilerOptions.target || ScriptTarget.ES3; @@ -106,11 +106,11 @@ namespace ts { isOptionalParameter }; - const tupleTypes: Map = {}; - const unionTypes: Map = {}; - const intersectionTypes: Map = {}; - const stringLiteralTypes: Map = {}; - const numericLiteralTypes: Map = {}; + const tupleTypes = createMap(); + const unionTypes = createMap(); + const intersectionTypes = createMap(); + const stringLiteralTypes = createMap(); + const numericLiteralTypes = createMap(); const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown"); const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__"); @@ -132,7 +132,7 @@ namespace ts { const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - emptyGenericType.instantiations = {}; + emptyGenericType.instantiations = createMap(); const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); // The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated @@ -146,7 +146,7 @@ namespace ts { const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); - const globals: SymbolTable = {}; + const globals = createMap(); /** * List of every ambient module with a "*" wildcard. * Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches. @@ -215,7 +215,7 @@ namespace ts { const flowLoopKeys: string[] = []; const flowLoopTypes: Type[][] = []; const visitedFlowNodes: FlowNode[] = []; - const visitedFlowTypes: Type[] = []; + const visitedFlowTypes: FlowType[] = []; const potentialThisCollisions: Node[] = []; const awaitedTypeStack: number[] = []; @@ -245,7 +245,8 @@ namespace ts { NEUndefinedOrNull = 1 << 19, // x != undefined / x != null Truthy = 1 << 20, // x Falsy = 1 << 21, // !x - All = (1 << 22) - 1, + Discriminatable = 1 << 22, // May have discriminant property + All = (1 << 23) - 1, // The following members encode facts about particular kinds of types for use in the getTypeFacts function. // The presence of a particular fact means that the given test is true for some (and possibly all) values // of that kind of type. @@ -275,15 +276,15 @@ namespace ts { TrueFacts = BaseBooleanFacts | Truthy, SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable, ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, - FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy, + FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable, FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy, UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy, NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy, } - const typeofEQFacts: Map = { + const typeofEQFacts = createMap({ "string": TypeFacts.TypeofEQString, "number": TypeFacts.TypeofEQNumber, "boolean": TypeFacts.TypeofEQBoolean, @@ -291,9 +292,9 @@ namespace ts { "undefined": TypeFacts.EQUndefined, "object": TypeFacts.TypeofEQObject, "function": TypeFacts.TypeofEQFunction - }; + }); - const typeofNEFacts: Map = { + const typeofNEFacts = createMap({ "string": TypeFacts.TypeofNEString, "number": TypeFacts.TypeofNENumber, "boolean": TypeFacts.TypeofNEBoolean, @@ -301,19 +302,19 @@ namespace ts { "undefined": TypeFacts.NEUndefined, "object": TypeFacts.TypeofNEObject, "function": TypeFacts.TypeofNEFunction - }; + }); - const typeofTypesByName: Map = { + const typeofTypesByName = createMap({ "string": stringType, "number": numberType, "boolean": booleanType, "symbol": esSymbolType, "undefined": undefinedType - }; + }); let jsxElementType: ObjectType; /** Things we lazy load from the JSX namespace */ - const jsxTypes: Map = {}; + const jsxTypes = createMap(); const JsxNames = { JSX: "JSX", IntrinsicElements: "IntrinsicElements", @@ -324,10 +325,10 @@ namespace ts { IntrinsicClassAttributes: "IntrinsicClassAttributes" }; - const subtypeRelation: Map = {}; - const assignableRelation: Map = {}; - const comparableRelation: Map = {}; - const identityRelation: Map = {}; + const subtypeRelation = createMap(); + const assignableRelation = createMap(); + const comparableRelation = createMap(); + const identityRelation = createMap(); // This is for caching the result of getSymbolDisplayBuilder. Do not access directly. let _displayBuilder: SymbolDisplayBuilder; @@ -341,9 +342,8 @@ namespace ts { ResolvedReturnType } - const builtinGlobals: SymbolTable = { - [undefinedSymbol.name]: undefinedSymbol - }; + const builtinGlobals = createMap(); + builtinGlobals[undefinedSymbol.name] = undefinedSymbol; initializeTypeChecker(); @@ -403,8 +403,8 @@ namespace ts { result.parent = symbol.parent; if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration; if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true; - if (symbol.members) result.members = cloneSymbolTable(symbol.members); - if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports); + if (symbol.members) result.members = cloneMap(symbol.members); + if (symbol.exports) result.exports = cloneMap(symbol.exports); recordMergedSymbol(result, symbol); return result; } @@ -426,11 +426,11 @@ namespace ts { target.declarations.push(node); }); if (source.members) { - if (!target.members) target.members = {}; + if (!target.members) target.members = createMap(); mergeSymbolTable(target.members, source.members); } if (source.exports) { - if (!target.exports) target.exports = {}; + if (!target.exports) target.exports = createMap(); mergeSymbolTable(target.exports, source.exports); } recordMergedSymbol(target, source); @@ -447,29 +447,17 @@ namespace ts { } } - function cloneSymbolTable(symbolTable: SymbolTable): SymbolTable { - const result: SymbolTable = {}; - for (const id in symbolTable) { - if (hasProperty(symbolTable, id)) { - result[id] = symbolTable[id]; - } - } - return result; - } - function mergeSymbolTable(target: SymbolTable, source: SymbolTable) { for (const id in source) { - if (hasProperty(source, id)) { - if (!hasProperty(target, id)) { - target[id] = source[id]; - } - else { - let symbol = target[id]; - if (!(symbol.flags & SymbolFlags.Merged)) { - target[id] = symbol = cloneSymbol(symbol); - } - mergeSymbol(symbol, source[id]); + let targetSymbol = target[id]; + if (!targetSymbol) { + target[id] = source[id]; + } + else { + if (!(targetSymbol.flags & SymbolFlags.Merged)) { + target[id] = targetSymbol = cloneSymbol(targetSymbol); } + mergeSymbol(targetSymbol, source[id]); } } } @@ -513,14 +501,12 @@ namespace ts { function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) { for (const id in source) { - if (hasProperty(source, id)) { - if (hasProperty(target, id)) { - // Error on redeclarations - forEach(target[id].declarations, addDeclarationDiagnostic(id, message)); - } - else { - target[id] = source[id]; - } + if (target[id]) { + // Error on redeclarations + forEach(target[id].declarations, addDeclarationDiagnostic(id, message)); + } + else { + target[id] = source[id]; } } @@ -537,7 +523,7 @@ namespace ts { function getNodeLinks(node: Node): NodeLinks { const nodeId = getNodeId(node); - return nodeLinks[nodeId] || (nodeLinks[nodeId] = {}); + return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 }); } function isGlobalSourceFile(node: Node) { @@ -545,18 +531,20 @@ namespace ts { } function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol { - if (meaning && hasProperty(symbols, name)) { + if (meaning) { const symbol = symbols[name]; - Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); - if (symbol.flags & meaning) { - return symbol; - } - if (symbol.flags & SymbolFlags.Alias) { - const target = resolveAlias(symbol); - // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors - if (target === unknownSymbol || target.flags & meaning) { + if (symbol) { + Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here."); + if (symbol.flags & meaning) { return symbol; } + if (symbol.flags & SymbolFlags.Alias) { + const target = resolveAlias(symbol); + // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors + if (target === unknownSymbol || target.flags & meaning) { + return symbol; + } + } } } // return undefined if we can't find a symbol. @@ -664,7 +652,7 @@ namespace ts { // Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and // the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with // the given name can be found. - function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol { + function resolveName(location: Node | undefined, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol { let result: Symbol; let lastLocation: Node; let propertyWithInvalidInitializer: Node; @@ -744,7 +732,7 @@ namespace ts { // 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely* // an alias. If we used &, we'd be throwing out symbols that have non alias aspects, // which is not the desired behavior. - if (hasProperty(moduleExports, name) && + if (moduleExports[name] && moduleExports[name].flags === SymbolFlags.Alias && getDeclarationOfKind(moduleExports[name], SyntaxKind.ExportSpecifier)) { break; @@ -881,7 +869,8 @@ namespace ts { if (!result) { if (nameNotFoundMessage) { - if (!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) && + if (!errorLocation || + !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) && !checkAndReportErrorForExtendingInterface(errorLocation)) { error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg)); } @@ -930,7 +919,7 @@ namespace ts { } function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: string, nameArg: string | Identifier): boolean { - if (!errorLocation || (errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(errorLocation)) || isInTypeQuery(errorLocation))) { + if ((errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(errorLocation)) || isInTypeQuery(errorLocation))) { return false; } @@ -968,28 +957,30 @@ namespace ts { function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean { - let parentClassExpression = errorLocation; - while (parentClassExpression) { - const kind = parentClassExpression.kind; - if (kind === SyntaxKind.Identifier || kind === SyntaxKind.PropertyAccessExpression) { - parentClassExpression = parentClassExpression.parent; - continue; - } - if (kind === SyntaxKind.ExpressionWithTypeArguments) { - break; - } - return false; - } - if (!parentClassExpression) { - return false; - } - const expression = (parentClassExpression).expression; - if (resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) { + const expression = getEntityNameForExtendingInterface(errorLocation); + const isError = !!(expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)); + if (isError) { error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression)); - return true; } - return false; + return isError; } + /** + * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression, + * but returns undefined if that expression is not an EntityNameExpression. + */ + function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.PropertyAccessExpression: + return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined; + case SyntaxKind.ExpressionWithTypeArguments: + Debug.assert(isEntityNameExpression((node).expression)); + return (node).expression; + default: + return undefined; + } + } + function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void { Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0); @@ -1033,7 +1024,7 @@ namespace ts { } function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration { - return forEach(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined); + return find(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined); } function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol { @@ -1101,9 +1092,9 @@ namespace ts { function getExportOfModule(symbol: Symbol, name: string): Symbol { if (symbol.flags & SymbolFlags.Module) { - const exports = getExportsOfSymbol(symbol); - if (hasProperty(exports, name)) { - return resolveSymbol(exports[name]); + const exportedSymbol = getExportsOfSymbol(symbol)[name]; + if (exportedSymbol) { + return resolveSymbol(exportedSymbol); } } } @@ -1168,7 +1159,7 @@ namespace ts { } function getTargetOfExportAssignment(node: ExportAssignment): Symbol { - return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); + return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace); } function getTargetOfAliasDeclaration(node: Declaration): Symbol { @@ -1278,7 +1269,7 @@ namespace ts { } // Resolves a qualified name and any involved aliases - function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol { + function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol | undefined { if (nodeIsMissing(name)) { return undefined; } @@ -1293,7 +1284,7 @@ namespace ts { } } else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) { - const left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression; + const left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression; const right = name.kind === SyntaxKind.QualifiedName ? (name).right : (name).name; const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors); @@ -1421,7 +1412,7 @@ namespace ts { */ function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map, exportNode?: ExportDeclaration) { for (const id in source) { - if (id !== "default" && !hasProperty(target, id)) { + if (id !== "default" && !target[id]) { target[id] = source[id]; if (lookupTable && exportNode) { lookupTable[id] = { @@ -1429,7 +1420,7 @@ namespace ts { } as ExportCollisionTracker; } } - else if (lookupTable && exportNode && id !== "default" && hasProperty(target, id) && resolveSymbol(target[id]) !== resolveSymbol(source[id])) { + else if (lookupTable && exportNode && id !== "default" && target[id] && resolveSymbol(target[id]) !== resolveSymbol(source[id])) { if (!lookupTable[id].exportsWithDuplicate) { lookupTable[id].exportsWithDuplicate = [exportNode]; } @@ -1451,12 +1442,12 @@ namespace ts { return; } visitedSymbols.push(symbol); - const symbols = cloneSymbolTable(symbol.exports); + const symbols = cloneMap(symbol.exports); // All export * declarations are collected in an __export symbol by the binder const exportStars = symbol.exports["__export"]; if (exportStars) { - const nestedSymbols: SymbolTable = {}; - const lookupTable: Map = {}; + const nestedSymbols = createMap(); + const lookupTable = createMap(); for (const node of exportStars.declarations) { const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier); const exportedSymbols = visit(resolvedModule); @@ -1470,7 +1461,7 @@ namespace ts { for (const id in lookupTable) { const { exportsWithDuplicate } = lookupTable[id]; // It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself - if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || hasProperty(symbols, id)) { + if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols[id]) { continue; } for (const node of exportsWithDuplicate) { @@ -1538,8 +1529,8 @@ namespace ts { function createType(flags: TypeFlags): Type { const result = new Type(checker, flags); - result.id = typeCount; typeCount++; + result.id = typeCount; return result; } @@ -1576,13 +1567,11 @@ namespace ts { function getNamedMembers(members: SymbolTable): Symbol[] { let result: Symbol[]; for (const id in members) { - if (hasProperty(members, id)) { - if (!isReservedMemberName(id)) { - if (!result) result = []; - const symbol = members[id]; - if (symbolIsValue(symbol)) { - result.push(symbol); - } + if (!isReservedMemberName(id)) { + if (!result) result = []; + const symbol = members[id]; + if (symbolIsValue(symbol)) { + result.push(symbol); } } } @@ -1658,12 +1647,12 @@ namespace ts { } // If symbol is directly available by its name in the symbol table - if (isAccessible(lookUp(symbols, symbol.name))) { + if (isAccessible(symbols[symbol.name])) { return [symbol]; } // Check if symbol is any of the alias - return forEachValue(symbols, symbolFromSymbolTable => { + return forEachProperty(symbols, symbolFromSymbolTable => { if (symbolFromSymbolTable.flags & SymbolFlags.Alias && symbolFromSymbolTable.name !== "export=" && !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) { @@ -1698,12 +1687,12 @@ namespace ts { let qualify = false; forEachSymbolTableInScope(enclosingDeclaration, symbolTable => { // If symbol of this name is not available in the symbol table we are ok - if (!hasProperty(symbolTable, symbol.name)) { + let symbolFromSymbolTable = symbolTable[symbol.name]; + if (!symbolFromSymbolTable) { // Continue to the next symbol table return false; } // If the symbol with this name is present it should refer to the symbol - let symbolFromSymbolTable = symbolTable[symbol.name]; if (symbolFromSymbolTable === symbol) { // No need to qualify return true; @@ -1849,7 +1838,7 @@ namespace ts { } } - function isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult { + function isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult { // get symbol of the first identifier of the entityName let meaning: SymbolFlags; if (entityName.parent.kind === SyntaxKind.TypeQuery || isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) { @@ -3121,7 +3110,7 @@ namespace ts { // Return the type implied by an object binding pattern function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type { - const members: SymbolTable = {}; + const members = createMap(); let hasComputedProperties = false; forEach(pattern.elements, e => { const name = e.propertyName || e.name; @@ -3251,6 +3240,13 @@ namespace ts { // * className.prototype.method = expr if (declaration.kind === SyntaxKind.BinaryExpression || declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) { + // Use JS Doc type if present on parent expression statement + if (declaration.flags & NodeFlags.JavaScriptFile) { + const typeTag = getJSDocTypeTag(declaration.parent); + if (typeTag && typeTag.typeExpression) { + return links.type = getTypeFromTypeNode(typeTag.typeExpression.type); + } + } const declaredTypes = map(symbol.declarations, decl => decl.kind === SyntaxKind.BinaryExpression ? checkExpressionCached((decl).right) : @@ -3679,7 +3675,7 @@ namespace ts { const baseTypeNodes = getInterfaceBaseTypeNodes(declaration); if (baseTypeNodes) { for (const node of baseTypeNodes) { - if (isSupportedExpressionWithTypeArguments(node)) { + if (isEntityNameExpression(node.expression)) { const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true); if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) { return false; @@ -3709,7 +3705,7 @@ namespace ts { type.typeParameters = concatenate(outerTypeParameters, localTypeParameters); type.outerTypeParameters = outerTypeParameters; type.localTypeParameters = localTypeParameters; - (type).instantiations = {}; + (type).instantiations = createMap(); (type).instantiations[getTypeListId(type.typeParameters)] = type; (type).target = type; (type).typeArguments = type.typeParameters; @@ -3751,7 +3747,7 @@ namespace ts { if (typeParameters) { // Initialize the instantiation cache for generic type aliases. The declared type corresponds to // an instantiation of the type alias with the type parameters supplied as type arguments. - links.instantiations = {}; + links.instantiations = createMap(); links.instantiations[getTypeListId(links.typeParameters)] = type; } } @@ -3772,7 +3768,7 @@ namespace ts { return expr.kind === SyntaxKind.NumericLiteral || expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken && (expr).operand.kind === SyntaxKind.NumericLiteral || - expr.kind === SyntaxKind.Identifier && hasProperty(symbol.exports, (expr).text); + expr.kind === SyntaxKind.Identifier && !!symbol.exports[(expr).text]; } function enumHasLiteralMembers(symbol: Symbol) { @@ -3795,7 +3791,7 @@ namespace ts { enumType.symbol = symbol; if (enumHasLiteralMembers(symbol)) { const memberTypeList: Type[] = []; - const memberTypes: Map = {}; + const memberTypes = createMap(); for (const declaration of enumType.symbol.declarations) { if (declaration.kind === SyntaxKind.EnumDeclaration) { computeEnumMemberValues(declaration); @@ -3958,7 +3954,7 @@ namespace ts { } function createSymbolTable(symbols: Symbol[]): SymbolTable { - const result: SymbolTable = {}; + const result = createMap(); for (const symbol of symbols) { result[symbol.name] = symbol; } @@ -3968,7 +3964,7 @@ namespace ts { // The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true, // we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation. function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable { - const result: SymbolTable = {}; + const result = createMap(); for (const symbol of symbols) { result[symbol.name] = mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper); } @@ -3977,7 +3973,7 @@ namespace ts { function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) { for (const s of baseSymbols) { - if (!hasProperty(symbols, s.name)) { + if (!symbols[s.name]) { symbols[s.name] = s; } } @@ -4000,6 +3996,9 @@ namespace ts { return createTypeReference((type).target, concatenate((type).typeArguments, [thisArgument || (type).target.thisType])); } + if (type.flags & TypeFlags.Tuple) { + return createTupleType((type as TupleType).elementTypes, thisArgument); + } return type; } @@ -4091,7 +4090,7 @@ namespace ts { } function createTupleTypeMemberSymbols(memberTypes: Type[]): SymbolTable { - const members: SymbolTable = {}; + const members = createMap(); for (let i = 0; i < memberTypes.length; i++) { const symbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i); symbol.type = memberTypes[i]; @@ -4103,7 +4102,8 @@ namespace ts { function resolveTupleTypeMembers(type: TupleType) { const arrayElementType = getUnionType(type.elementTypes); // Make the tuple type itself the 'this' type by including an extra type argument - const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type])); + // (Unless it's provided in the case that the tuple is a type parameter constraint) + const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type.thisType || type])); const members = createTupleTypeMemberSymbols(type.elementTypes); addInheritedMembers(members, arrayType.properties); setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexInfo, arrayType.numberIndexInfo); @@ -4313,11 +4313,9 @@ namespace ts { function getPropertyOfObjectType(type: Type, name: string): Symbol { if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - if (hasProperty(resolved.members, name)) { - const symbol = resolved.members[name]; - if (symbolIsValue(symbol)) { - return symbol; - } + const symbol = resolved.members[name]; + if (symbol && symbolIsValue(symbol)) { + return symbol; } } } @@ -4416,10 +4414,19 @@ namespace ts { } const propTypes: Type[] = []; const declarations: Declaration[] = []; + let commonType: Type = undefined; + let hasCommonType = true; for (const prop of props) { if (prop.declarations) { addRange(declarations, prop.declarations); } + const type = getTypeOfSymbol(prop); + if (!commonType) { + commonType = type; + } + else if (type !== commonType) { + hasCommonType = false; + } propTypes.push(getTypeOfSymbol(prop)); } const result = createSymbol( @@ -4429,6 +4436,7 @@ namespace ts { commonFlags, name); result.containingType = containingType; + result.hasCommonType = hasCommonType; result.declarations = declarations; result.isReadonly = isReadonly; result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes); @@ -4436,29 +4444,32 @@ namespace ts { } function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol { - const properties = type.resolvedProperties || (type.resolvedProperties = {}); - if (hasProperty(properties, name)) { - return properties[name]; - } - const property = createUnionOrIntersectionProperty(type, name); - if (property) { - properties[name] = property; + const properties = type.resolvedProperties || (type.resolvedProperties = createMap()); + let property = properties[name]; + if (!property) { + property = createUnionOrIntersectionProperty(type, name); + if (property) { + properties[name] = property; + } } return property; } - // Return the symbol for the property with the given name in the given type. Creates synthetic union properties when - // necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from - // Object and Function as appropriate. + /** + * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when + * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from + * Object and Function as appropriate. + * + * @param type a type to look up property from + * @param name a name of property to look up in a given type + */ function getPropertyOfType(type: Type, name: string): Symbol { type = getApparentType(type); if (type.flags & TypeFlags.ObjectType) { const resolved = resolveStructuredTypeMembers(type); - if (hasProperty(resolved.members, name)) { - const symbol = resolved.members[name]; - if (symbolIsValue(symbol)) { - return symbol; - } + const symbol = resolved.members[name]; + if (symbol && symbolIsValue(symbol)) { + return symbol; } if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) { const symbol = getPropertyOfObjectType(globalFunctionType, name); @@ -4926,24 +4937,27 @@ namespace ts { } function getTypeListId(types: Type[]) { + let result = ""; if (types) { - switch (types.length) { - case 1: - return "" + types[0].id; - case 2: - return types[0].id + "," + types[1].id; - default: - let result = ""; - for (let i = 0; i < types.length; i++) { - if (i > 0) { - result += ","; - } - result += types[i].id; - } - return result; + const length = types.length; + let i = 0; + while (i < length) { + const startId = types[i].id; + let count = 1; + while (i + count < length && types[i + count].id === startId + count) { + count++; + } + if (result.length) { + result += ","; + } + result += startId; + if (count > 1) { + result += ":" + count; + } + i += count; } } - return ""; + return result; } // This function is used to propagate certain flags when creating new object type references and union types. @@ -5026,7 +5040,7 @@ namespace ts { return getDeclaredTypeOfSymbol(symbol); } - function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): LeftHandSideExpression | EntityName { + function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): EntityNameOrEntityNameExpression | undefined { switch (node.kind) { case SyntaxKind.TypeReference: return (node).typeName; @@ -5035,8 +5049,9 @@ namespace ts { case SyntaxKind.ExpressionWithTypeArguments: // We only support expressions that are simple qualified names. For other // expressions this produces undefined. - if (isSupportedExpressionWithTypeArguments(node)) { - return (node).expression; + const expr = (node).expression; + if (isEntityNameExpression(expr)) { + return expr; } // fall through; @@ -5047,7 +5062,7 @@ namespace ts { function resolveTypeReferenceName( node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference, - typeReferenceName: LeftHandSideExpression | EntityName) { + typeReferenceName: EntityNameExpression | EntityName) { if (!typeReferenceName) { return unknownSymbol; @@ -5088,15 +5103,14 @@ namespace ts { const typeReferenceName = getTypeReferenceName(node); symbol = resolveTypeReferenceName(node, typeReferenceName); type = getTypeReferenceType(node, symbol); - - links.resolvedSymbol = symbol; - links.resolvedType = type; } else { // We only support expressions that are simple qualified names. For other expressions this produces undefined. - const typeNameOrExpression = node.kind === SyntaxKind.TypeReference ? (node).typeName : - isSupportedExpressionWithTypeArguments(node) ? (node).expression : - undefined; + const typeNameOrExpression: EntityNameOrEntityNameExpression = node.kind === SyntaxKind.TypeReference + ? (node).typeName + : isEntityNameExpression((node).expression) + ? (node).expression + : undefined; symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol; type = symbol === unknownSymbol ? unknownType : symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? getTypeFromClassOrInterfaceReference(node, symbol) : @@ -5215,15 +5229,16 @@ namespace ts { return links.resolvedType; } - function createTupleType(elementTypes: Type[]) { - const id = getTypeListId(elementTypes); - return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes)); + function createTupleType(elementTypes: Type[], thisType?: Type) { + const id = getTypeListId(elementTypes) + "," + (thisType ? thisType.id : 0); + return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes, thisType)); } - function createNewTupleType(elementTypes: Type[]) { + function createNewTupleType(elementTypes: Type[], thisType?: Type) { const propagatedFlags = getPropagatingFlagsOfTypes(elementTypes, /*excludeKinds*/ 0); const type = createObjectType(TypeFlags.Tuple | propagatedFlags); type.elementTypes = elementTypes; + type.thisType = thisType; return type; } @@ -5325,12 +5340,12 @@ namespace ts { } } - // We deduplicate the constituent types based on object identity. If the subtypeReduction flag is - // specified we also reduce the constituent type set to only include types that aren't subtypes of - // other types. Subtype reduction is expensive for large union types and is possible only when union + // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction + // flag is specified we also reduce the constituent type set to only include types that aren't subtypes + // of other types. Subtype reduction is expensive for large union types and is possible only when union // types are known not to circularly reference themselves (as is the case with union types created by // expression constructs such as array literals and the || and ?: operators). Named types can - // circularly reference themselves and therefore cannot be deduplicated during their declaration. + // circularly reference themselves and therefore cannot be subtype reduced during their declaration. // For example, "type Item = string | (() => Item" is a named type that circularly references itself. function getUnionType(types: Type[], subtypeReduction?: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { if (types.length === 0) { @@ -5352,15 +5367,23 @@ namespace ts { typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType : neverType; } - else if (typeSet.length === 1) { - return typeSet[0]; + return getUnionTypeFromSortedList(typeSet, aliasSymbol, aliasTypeArguments); + } + + // This function assumes the constituent type list is sorted and deduplicated. + function getUnionTypeFromSortedList(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type { + if (types.length === 0) { + return neverType; } - const id = getTypeListId(typeSet); + if (types.length === 1) { + return types[0]; + } + const id = getTypeListId(types); let type = unionTypes[id]; if (!type) { - const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable); + const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); type = unionTypes[id] = createObjectType(TypeFlags.Union | propagatedFlags); - type.types = typeSet; + type.types = types; type.aliasSymbol = aliasSymbol; type.aliasTypeArguments = aliasTypeArguments; } @@ -5452,7 +5475,7 @@ namespace ts { function getLiteralTypeForText(flags: TypeFlags, text: string) { const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes; - return hasProperty(map, text) ? map[text] : map[text] = createLiteralType(flags, text); + return map[text] || (map[text] = createLiteralType(flags, text)); } function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type { @@ -5909,6 +5932,13 @@ namespace ts { return isTypeRelatedTo(source, target, assignableRelation); } + // A type S is considered to be an instance of a type T if S and T are the same type or if S is a + // subtype of T but not structurally identical to T. This specifically means that two distinct but + // structurally identical types (such as two classes) are not considered instances of each other. + function isTypeInstanceOf(source: Type, target: Type): boolean { + return source === target || isTypeSubtypeOf(source, target) && !isTypeIdenticalTo(source, target); + } + /** * This is *not* a bi-directional relationship. * If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'. @@ -6560,7 +6590,7 @@ namespace ts { } sourceStack[depth] = source; targetStack[depth] = target; - maybeStack[depth] = {}; + maybeStack[depth] = createMap(); maybeStack[depth][id] = RelationComparisonResult.Succeeded; depth++; const saveExpandingFlags = expandingFlags; @@ -6591,7 +6621,7 @@ namespace ts { const maybeCache = maybeStack[depth]; // If result is definitely true, copy assumptions to global cache, else copy to next level up const destinationCache = (result === Ternary.True || depth === 0) ? relation : maybeStack[depth - 1]; - copyMap(maybeCache, destinationCache); + copyProperties(maybeCache, destinationCache); } else { // A false result goes straight into global cache (when something is false under assumptions it @@ -7201,7 +7231,7 @@ namespace ts { } function transformTypeOfMembers(type: Type, f: (propertyType: Type) => Type) { - const members: SymbolTable = {}; + const members = createMap(); for (const property of getPropertiesOfObjectType(type)) { const original = getTypeOfSymbol(property); const updated = f(original); @@ -7425,7 +7455,7 @@ namespace ts { let targetStack: Type[]; let depth = 0; let inferiority = 0; - const visited: Map = {}; + const visited = createMap(); inferFromTypes(source, target); function isInProcess(source: Type, target: Type) { @@ -7559,7 +7589,7 @@ namespace ts { return; } const key = source.id + "," + target.id; - if (hasProperty(visited, key)) { + if (visited[key]) { return; } visited[key] = true; @@ -7793,8 +7823,49 @@ namespace ts { return false; } - function rootContainsMatchingReference(source: Node, target: Node) { - return target.kind === SyntaxKind.PropertyAccessExpression && containsMatchingReference(source, (target).expression); + // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared + // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property + // a possible discriminant if its type differs in the constituents of containing union type, and if every + // choice is a unit type or a union of unit types. + function containsMatchingReferenceDiscriminant(source: Node, target: Node) { + return target.kind === SyntaxKind.PropertyAccessExpression && + containsMatchingReference(source, (target).expression) && + isDiscriminantProperty(getDeclaredTypeOfReference((target).expression), (target).name.text); + } + + function getDeclaredTypeOfReference(expr: Node): Type { + if (expr.kind === SyntaxKind.Identifier) { + return getTypeOfSymbol(getResolvedSymbol(expr)); + } + if (expr.kind === SyntaxKind.PropertyAccessExpression) { + const type = getDeclaredTypeOfReference((expr).expression); + return type && getTypeOfPropertyOfType(type, (expr).name.text); + } + return undefined; + } + + function isDiscriminantProperty(type: Type, name: string) { + if (type && type.flags & TypeFlags.Union) { + let prop = getPropertyOfType(type, name); + if (!prop) { + // The type may be a union that includes nullable or primitive types. If filtering + // those out produces a different type, get the property from that type instead. + // Effectively, we're checking if this *could* be a discriminant property once nullable + // and primitive types are removed by other type guards. + const filteredType = getTypeWithFacts(type, TypeFacts.Discriminatable); + if (filteredType !== type && filteredType.flags & TypeFlags.Union) { + prop = getPropertyOfType(filteredType, name); + } + } + if (prop && prop.flags & SymbolFlags.SyntheticProperty) { + if ((prop).isDiscriminantProperty === undefined) { + (prop).isDiscriminantProperty = !(prop).hasCommonType && + isUnitUnionType(getTypeOfSymbol(prop)); + } + return (prop).isDiscriminantProperty; + } + } + return false; } function isOrContainsMatchingReference(source: Node, target: Node) { @@ -7840,10 +7911,10 @@ namespace ts { // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, // we remove type string. function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) { - if (declaredType !== assignedType && declaredType.flags & TypeFlags.Union) { - const reducedTypes = filter(declaredType.types, t => typeMaybeAssignableTo(assignedType, t)); - if (reducedTypes.length) { - return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes); + if (declaredType !== assignedType) { + const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t)); + if (reducedType !== neverType) { + return reducedType; } } return declaredType; @@ -7857,6 +7928,14 @@ namespace ts { return result; } + function isFunctionObjectType(type: ObjectType): boolean { + // We do a quick check for a "bind" property before performing the more expensive subtype + // check. This gives us a quicker out in the common case where an object type is not a function. + const resolved = resolveStructuredTypeMembers(type); + return !!(resolved.callSignatures.length || resolved.constructSignatures.length || + resolved.members["bind"] && isTypeSubtypeOf(type, globalFunctionType)); + } + function getTypeFacts(type: Type): TypeFacts { const flags = type.flags; if (flags & TypeFlags.String) { @@ -7885,8 +7964,7 @@ namespace ts { type === falseType ? TypeFacts.FalseFacts : TypeFacts.TrueFacts; } if (flags & TypeFlags.ObjectType) { - const resolved = resolveStructuredTypeMembers(type); - return resolved.callSignatures.length || resolved.constructSignatures.length || isTypeSubtypeOf(type, globalFunctionType) ? + return isFunctionObjectType(type) ? strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts : strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts; } @@ -7901,7 +7979,7 @@ namespace ts { } if (flags & TypeFlags.TypeParameter) { const constraint = getConstraintOfTypeParameter(type); - return constraint ? getTypeFacts(constraint) : TypeFacts.All; + return getTypeFacts(constraint || emptyObjectType); } if (flags & TypeFlags.UnionOrIntersection) { return getTypeFactsOfTypes((type).types); @@ -7910,25 +7988,7 @@ namespace ts { } function getTypeWithFacts(type: Type, include: TypeFacts) { - if (!(type.flags & TypeFlags.Union)) { - return getTypeFacts(type) & include ? type : neverType; - } - let firstType: Type; - let types: Type[]; - for (const t of (type as UnionType).types) { - if (getTypeFacts(t) & include) { - if (!firstType) { - firstType = t; - } - else { - if (!types) { - types = [firstType]; - } - types.push(t); - } - } - } - return firstType ? types ? getUnionType(types) : firstType : neverType; + return filterType(type, t => (getTypeFacts(t) & include) !== 0); } function getTypeWithDefault(type: Type, defaultExpression: Expression) { @@ -8084,27 +8144,61 @@ namespace ts { return source.flags & TypeFlags.Union ? !forEach((source).types, t => !contains(types, t)) : contains(types, source); } - function filterType(type: Type, f: (t: Type) => boolean): Type { - return type.flags & TypeFlags.Union ? - getUnionType(filter((type).types, f)) : - f(type) ? type : neverType; + function isTypeSubsetOf(source: Type, target: Type) { + return source === target || target.flags & TypeFlags.Union && isTypeSubsetOfUnion(source, target); } - function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) { + function isTypeSubsetOfUnion(source: Type, target: UnionType) { + if (source.flags & TypeFlags.Union) { + for (const t of (source).types) { + if (!containsType(target.types, t)) { + return false; + } + } + return true; + } + if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.Enum && (source).baseType === target) { + return true; + } + return containsType(target.types, source); + } + + function filterType(type: Type, f: (t: Type) => boolean): Type { + if (type.flags & TypeFlags.Union) { + const types = (type).types; + const filtered = filter(types, f); + return filtered === types ? type : getUnionTypeFromSortedList(filtered); + } + return f(type) ? type : neverType; + } + + function isIncomplete(flowType: FlowType) { + return flowType.flags === 0; + } + + function getTypeFromFlowType(flowType: FlowType) { + return flowType.flags === 0 ? (flowType).type : flowType; + } + + function createFlowType(type: Type, incomplete: boolean): FlowType { + return incomplete ? { flags: 0, type } : type; + } + + function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node) { let key: string; if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } const initialType = assumeInitialized ? declaredType : includeFalsyTypes(declaredType, TypeFlags.Undefined); const visitedFlowStart = visitedFlowCount; - const result = getTypeAtFlowNode(reference.flowNode); + const result = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode)); visitedFlowCount = visitedFlowStart; if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(result, TypeFacts.NEUndefinedOrNull) === neverType) { return declaredType; } return result; - function getTypeAtFlowNode(flow: FlowNode): Type { + function getTypeAtFlowNode(flow: FlowNode): FlowType { while (true) { if (flow.flags & FlowFlags.Shared) { // We cache results of flow type resolution for shared nodes that were previously visited in @@ -8116,7 +8210,7 @@ namespace ts { } } } - let type: Type; + let type: FlowType; if (flow.flags & FlowFlags.Assignment) { type = getTypeAtFlowAssignment(flow); if (!type) { @@ -8142,7 +8236,7 @@ namespace ts { else if (flow.flags & FlowFlags.Start) { // Check if we should continue with the control flow of the containing function. const container = (flow).container; - if (container && includeOuterFunctions) { + if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression) { flow = container.flowNode; continue; } @@ -8184,41 +8278,45 @@ namespace ts { return undefined; } - function getTypeAtFlowCondition(flow: FlowCondition) { - let type = getTypeAtFlowNode(flow.antecedent); + function getTypeAtFlowCondition(flow: FlowCondition): FlowType { + const flowType = getTypeAtFlowNode(flow.antecedent); + let type = getTypeFromFlowType(flowType); if (type !== neverType) { // If we have an antecedent type (meaning we're reachable in some way), we first - // attempt to narrow the antecedent type. If that produces the nothing type, then - // we take the type guard as an indication that control could reach here in a - // manner not understood by the control flow analyzer (e.g. a function argument - // has an invalid type, or a nested function has possibly made an assignment to a - // captured variable). We proceed by reverting to the declared type and then + // attempt to narrow the antecedent type. If that produces the never type, and if + // the antecedent type is incomplete (i.e. a transient type in a loop), then we + // take the type guard as an indication that control *could* reach here once we + // have the complete type. We proceed by reverting to the declared type and then // narrow that. const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0; type = narrowType(type, flow.expression, assumeTrue); - if (type === neverType) { + if (type === neverType && isIncomplete(flowType)) { type = narrowType(declaredType, flow.expression, assumeTrue); } } - return type; + return createFlowType(type, isIncomplete(flowType)); } - function getTypeAtSwitchClause(flow: FlowSwitchClause) { - const type = getTypeAtFlowNode(flow.antecedent); + function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType { + const flowType = getTypeAtFlowNode(flow.antecedent); + let type = getTypeFromFlowType(flowType); const expr = flow.switchStatement.expression; if (isMatchingReference(reference, expr)) { - return narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); + type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } - if (isMatchingPropertyAccess(expr)) { - return narrowTypeByDiscriminant(type, expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); + else if (isMatchingReferenceDiscriminant(expr)) { + type = narrowTypeByDiscriminant(type, expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd)); } - return type; + return createFlowType(type, isIncomplete(flowType)); } - function getTypeAtFlowBranchLabel(flow: FlowLabel) { + function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType { const antecedentTypes: Type[] = []; + let subtypeReduction = false; + let seenIncomplete = false; for (const antecedent of flow.antecedents) { - const type = getTypeAtFlowNode(antecedent); + const flowType = getTypeAtFlowNode(antecedent); + const type = getTypeFromFlowType(flowType); // If the type at a particular antecedent path is the declared type and the // reference is known to always be assigned (i.e. when declared and initial types // are the same), there is no reason to process more antecedents since the only @@ -8229,15 +8327,24 @@ namespace ts { if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } + if (isIncomplete(flowType)) { + seenIncomplete = true; + } } - return getUnionType(antecedentTypes); + return createFlowType(getUnionType(antecedentTypes, subtypeReduction), seenIncomplete); } - function getTypeAtFlowLoopLabel(flow: FlowLabel) { + function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType { // If we have previously computed the control flow type for the reference at // this flow loop junction, return the cached type. const id = getFlowNodeId(flow); - const cache = flowLoopCaches[id] || (flowLoopCaches[id] = {}); + const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap()); if (!key) { key = getFlowCacheKey(reference); } @@ -8245,23 +8352,24 @@ namespace ts { return cache[key]; } // If this flow loop junction and reference are already being processed, return - // the union of the types computed for each branch so far. We should never see - // an empty array here because the first antecedent of a loop junction is always - // the non-looping control flow path that leads to the top. + // the union of the types computed for each branch so far, marked as incomplete. + // We should never see an empty array here because the first antecedent of a loop + // junction is always the non-looping control flow path that leads to the top. for (let i = flowLoopStart; i < flowLoopCount; i++) { if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key) { - return getUnionType(flowLoopTypes[i]); + return createFlowType(getUnionType(flowLoopTypes[i]), /*incomplete*/ true); } } // Add the flow loop junction and reference to the in-process stack and analyze // each antecedent code path. const antecedentTypes: Type[] = []; + let subtypeReduction = false; flowLoopNodes[flowLoopCount] = flow; flowLoopKeys[flowLoopCount] = key; flowLoopTypes[flowLoopCount] = antecedentTypes; for (const antecedent of flow.antecedents) { flowLoopCount++; - const type = getTypeAtFlowNode(antecedent); + const type = getTypeFromFlowType(getTypeAtFlowNode(antecedent)); flowLoopCount--; // If we see a value appear in the cache it is a sign that control flow analysis // was restarted and completed by checkExpressionCached. We can simply pick up @@ -8272,6 +8380,12 @@ namespace ts { if (!contains(antecedentTypes, type)) { antecedentTypes.push(type); } + // If an antecedent type is not a subset of the declared type, we need to perform + // subtype reduction. This happens when a "foreign" type is injected into the control + // flow using the instanceof operator or a user defined type predicate. + if (!isTypeSubsetOf(type, declaredType)) { + subtypeReduction = true; + } // If the type at a particular antecedent path is the declared type there is no // reason to process more antecedents since the only possible outcome is subtypes // that will be removed in the final union type anyway. @@ -8279,13 +8393,14 @@ namespace ts { break; } } - return cache[key] = getUnionType(antecedentTypes); + return cache[key] = getUnionType(antecedentTypes, subtypeReduction); } - function isMatchingPropertyAccess(expr: Expression) { + function isMatchingReferenceDiscriminant(expr: Expression) { return expr.kind === SyntaxKind.PropertyAccessExpression && + declaredType.flags & TypeFlags.Union && isMatchingReference(reference, (expr).expression) && - (declaredType.flags & TypeFlags.Union) !== 0; + isDiscriminantProperty(declaredType, (expr).name.text); } function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type { @@ -8299,10 +8414,10 @@ namespace ts { if (isMatchingReference(reference, expr)) { return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy); } - if (isMatchingPropertyAccess(expr)) { + if (isMatchingReferenceDiscriminant(expr)) { return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } - if (rootContainsMatchingReference(reference, expr)) { + if (containsMatchingReferenceDiscriminant(reference, expr)) { return declaredType; } return type; @@ -8331,13 +8446,13 @@ namespace ts { if (isMatchingReference(reference, right)) { return narrowTypeByEquality(type, operator, left, assumeTrue); } - if (isMatchingPropertyAccess(left)) { + if (isMatchingReferenceDiscriminant(left)) { return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); } - if (isMatchingPropertyAccess(right)) { + if (isMatchingReferenceDiscriminant(right)) { return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); } - if (rootContainsMatchingReference(reference, left) || rootContainsMatchingReference(reference, right)) { + if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) { return declaredType; } break; @@ -8392,16 +8507,16 @@ namespace ts { } if (assumeTrue && !(type.flags & TypeFlags.Union)) { // We narrow a non-union type to an exact primitive type if the non-union type - // is a supertype of that primtive type. For example, type 'any' can be narrowed + // is a supertype of that primitive type. For example, type 'any' can be narrowed // to one of the primitive types. - const targetType = getProperty(typeofTypesByName, literal.text); + const targetType = typeofTypesByName[literal.text]; if (targetType && isTypeSubtypeOf(targetType, type)) { return targetType; } } const facts = assumeTrue ? - getProperty(typeofEQFacts, literal.text) || TypeFacts.TypeofEQHostObject : - getProperty(typeofNEFacts, literal.text) || TypeFacts.TypeofNEHostObject; + typeofEQFacts[literal.text] || TypeFacts.TypeofEQHostObject : + typeofNEFacts[literal.text] || TypeFacts.TypeofNEHostObject; return getTypeWithFacts(type, facts); } @@ -8432,10 +8547,6 @@ namespace ts { } return type; } - // We never narrow type any in an instanceof guard - if (isTypeAny(type)) { - return type; - } // Check that right operand is a function type with a prototype property const rightType = checkExpression(expr.right); @@ -8453,6 +8564,11 @@ namespace ts { } } + // Don't narrow from 'any' if the target type is exactly 'Object' or 'Function' + if (isTypeAny(type) && (targetType === globalObjectType || targetType === globalFunctionType)) { + return type; + } + if (!targetType) { // Target type is type of construct signature let constructSignatures: Signature[]; @@ -8476,29 +8592,30 @@ namespace ts { function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean) { if (!assumeTrue) { - return type.flags & TypeFlags.Union ? - getUnionType(filter((type).types, t => !isTypeSubtypeOf(t, candidate))) : - type; + return filterType(type, t => !isTypeInstanceOf(t, candidate)); } - // If the current type is a union type, remove all constituents that aren't assignable to + // If the current type is a union type, remove all constituents that couldn't be instances of // the candidate type. If one or more constituents remain, return a union of those. if (type.flags & TypeFlags.Union) { - const assignableConstituents = filter((type).types, t => isTypeAssignableTo(t, candidate)); - if (assignableConstituents.length) { - return getUnionType(assignableConstituents); + const assignableType = filterType(type, t => isTypeInstanceOf(t, candidate)); + if (assignableType !== neverType) { + return assignableType; } } - // If the candidate type is assignable to the target type, narrow to the candidate type. - // Otherwise, if the current type is assignable to the candidate, keep the current type. - // Otherwise, the types are completely unrelated, so narrow to the empty type. + // If the candidate type is a subtype of the target type, narrow to the candidate type. + // Otherwise, if the target type is assignable to the candidate type, keep the target type. + // Otherwise, if the candidate type is assignable to the target type, narrow to the candidate + // type. Otherwise, the types are completely unrelated, so narrow to an intersection of the + // two types. const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; - return isTypeAssignableTo(candidate, targetType) ? candidate : + return isTypeSubtypeOf(candidate, targetType) ? candidate : isTypeAssignableTo(type, candidate) ? type : - getIntersectionType([type, candidate]); + isTypeAssignableTo(candidate, targetType) ? candidate : + getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { - if (type.flags & TypeFlags.Any || !hasMatchingArgument(callExpression, reference)) { + if (!hasMatchingArgument(callExpression, reference)) { return type; } const signature = getResolvedSignature(callExpression); @@ -8506,6 +8623,12 @@ namespace ts { if (!predicate) { return type; } + + // Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function' + if (isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType)) { + return type; + } + if (isIdentifierTypePredicate(predicate)) { const predicateArgument = callExpression.arguments[predicate.parameterIndex]; if (predicateArgument) { @@ -8591,21 +8714,52 @@ namespace ts { function getControlFlowContainer(node: Node): Node { while (true) { node = node.parent; - if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.PropertyDeclaration) { + if (isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) || + node.kind === SyntaxKind.ModuleBlock || + node.kind === SyntaxKind.SourceFile || + node.kind === SyntaxKind.PropertyDeclaration) { return node; } } } - function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) { - const declarationContainer = getControlFlowContainer(declaration); - let container = getControlFlowContainer(reference); - while (container !== declarationContainer && - (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) && - (includeOuterFunctions || getImmediatelyInvokedFunctionExpression(container))) { - container = getControlFlowContainer(container); + // Check if a parameter is assigned anywhere within its declaring function. + function isParameterAssigned(symbol: Symbol) { + const func = getRootDeclaration(symbol.valueDeclaration).parent; + const links = getNodeLinks(func); + if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) { + links.flags |= NodeCheckFlags.AssignmentsMarked; + if (!hasParentWithAssignmentsMarked(func)) { + markParameterAssignments(func); + } + } + return symbol.isAssigned || false; + } + + function hasParentWithAssignmentsMarked(node: Node) { + while (true) { + node = node.parent; + if (!node) { + return false; + } + if (isFunctionLike(node) && getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked) { + return true; + } + } + } + + function markParameterAssignments(node: Node) { + if (node.kind === SyntaxKind.Identifier) { + if (isAssignmentTarget(node)) { + const symbol = getResolvedSymbol(node); + if (symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter) { + symbol.isAssigned = true; + } + } + } + else { + forEachChild(node, markParameterAssignments); } - return container === declarationContainer; } function checkIdentifier(node: Identifier): Type { @@ -8660,15 +8814,35 @@ namespace ts { checkNestedBlockScopedBinding(node, symbol); const type = getTypeOfSymbol(localOrExportSymbol); - if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node)) { + const declaration = localOrExportSymbol.valueDeclaration; + // We only narrow variables and parameters occurring in a non-assignment position. For all other + // entities we simply return the declared type. + if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node) || !declaration) { return type; } - const declaration = localOrExportSymbol.valueDeclaration; - const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol); - const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration || - getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || - !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions); - const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions); + // The declaration container is the innermost function that encloses the declaration of the variable + // or parameter. The flow container is the innermost function starting with which we analyze the control + // flow graph to determine the control flow based type. + const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; + const declarationContainer = getControlFlowContainer(declaration); + let flowContainer = getControlFlowContainer(node); + // When the control flow originates in a function expression or arrow function and we are referencing + // a const variable or parameter from an outer function, we extend the origin of the control flow + // analysis to include the immediately enclosing function. + while (flowContainer !== declarationContainer && + (flowContainer.kind === SyntaxKind.FunctionExpression || flowContainer.kind === SyntaxKind.ArrowFunction) && + (isReadonlySymbol(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) { + flowContainer = getControlFlowContainer(flowContainer); + } + // We only look for uninitialized variables in strict null checking mode, and only when we can analyze + // the entire control flow graph from the variable's declaration (i.e. when the flow container and + // declaration container are the same). + const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter || + flowContainer !== declarationContainer || isInAmbientContext(declaration); + const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer); + // A variable is considered uninitialized when it is possible to analyze the entire control flow graph + // from declaration to use, and when the variable's declared type doesn't include undefined but the + // control flow based type does include undefined. if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors @@ -8921,7 +9095,7 @@ namespace ts { if (isClassLike(container.parent)) { const symbol = getSymbolOfNode(container.parent); const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (getDeclaredTypeOfSymbol(symbol)).thisType; - return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions*/ true); + return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*flowContainer*/ undefined); } if (isInJavaScriptFile(node)) { @@ -9332,6 +9506,11 @@ namespace ts { const binaryExpression = node.parent; const operator = binaryExpression.operatorToken.kind; if (operator >= SyntaxKind.FirstAssignment && operator <= SyntaxKind.LastAssignment) { + // Don't do this for special property assignments to avoid circularity + if (getSpecialPropertyAssignmentKind(binaryExpression) !== SpecialPropertyAssignmentKind.None) { + return undefined; + } + // In an assignment expression, the right operand is contextually typed by the type of the left operand. if (node === binaryExpression.right) { return checkExpression(binaryExpression.left); @@ -9823,7 +10002,7 @@ namespace ts { // Grammar checking checkGrammarObjectLiteralExpression(node, inDestructuringPattern); - const propertiesTable: SymbolTable = {}; + const propertiesTable = createMap(); const propertiesArray: Symbol[] = []; const contextualType = getApparentTypeOfContextualType(node); const contextualTypeHasPattern = contextualType && contextualType.pattern && @@ -9914,7 +10093,7 @@ namespace ts { // type with those properties for which the binding pattern specifies a default value. if (contextualTypeHasPattern) { for (const prop of getPropertiesOfType(contextualType)) { - if (!hasProperty(propertiesTable, prop.name)) { + if (!propertiesTable[prop.name]) { if (!(prop.flags & SymbolFlags.Optional)) { error(prop.valueDeclaration || (prop).bindingElement, Diagnostics.Initializer_provides_no_value_for_this_binding_element_and_the_binding_element_has_no_default_value); @@ -10004,10 +10183,9 @@ namespace ts { const correspondingPropSymbol = getPropertyOfType(elementAttributesType, node.name.text); correspondingPropType = correspondingPropSymbol && getTypeOfSymbol(correspondingPropSymbol); if (isUnhyphenatedJsxName(node.name.text)) { - // Maybe there's a string indexer? - const indexerType = getIndexTypeOfType(elementAttributesType, IndexKind.String); - if (indexerType) { - correspondingPropType = indexerType; + const attributeType = getTypeOfPropertyOfType(elementAttributesType, getTextOfPropertyName(node.name)) || getIndexTypeOfType(elementAttributesType, IndexKind.String); + if (attributeType) { + correspondingPropType = attributeType; } else { // If there's no corresponding property with this name, error @@ -10364,7 +10542,7 @@ namespace ts { const targetAttributesType = getJsxElementAttributesType(node); - const nameTable: Map = {}; + const nameTable = createMap(); // Process this array in right-to-left order so we know which // attributes (mostly from spreads) are being overwritten and // thus should have their types ignored @@ -10388,7 +10566,7 @@ namespace ts { const targetProperties = getPropertiesOfType(targetAttributesType); for (let i = 0; i < targetProperties.length; i++) { if (!(targetProperties[i].flags & SymbolFlags.Optional) && - nameTable[targetProperties[i].name] === undefined) { + !nameTable[targetProperties[i].name]) { error(node, Diagnostics.Property_0_is_missing_in_type_1, targetProperties[i].name, typeToString(targetAttributesType)); } @@ -10582,7 +10760,7 @@ namespace ts { !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) { return propType; } - return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions*/ false); + return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer*/ undefined); } function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean { @@ -10707,10 +10885,11 @@ namespace ts { } // Check for compatible indexer types. - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol)) { + const allowedNullableFlags = strictNullChecks ? 0 : TypeFlags.Nullable; + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbol | allowedNullableFlags)) { // Try to use a number indexer. - if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike) || isForInVariableForNumericPropertyNames(node.argumentExpression)) { + if (isTypeAnyOrAllConstituentTypesHaveKind(indexType, TypeFlags.NumberLike | allowedNullableFlags) || isForInVariableForNumericPropertyNames(node.argumentExpression)) { const numberIndexInfo = getIndexInfoOfType(objectType, IndexKind.Number); if (numberIndexInfo) { getNodeLinks(node).resolvedIndexInfo = numberIndexInfo; @@ -12837,6 +13016,9 @@ namespace ts { return checkDestructuringAssignment(element, type, contextualMapper); } else { + // We still need to check element expression here because we may need to set appropriate flag on the expression + // such as NodeCheckFlags.LexicalThis on "this"expression. + checkExpression(element); if (isTupleType(sourceType)) { error(element, Diagnostics.Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2, typeToString(sourceType), (sourceType).elementTypes.length, elements.length); } @@ -13667,15 +13849,19 @@ namespace ts { } function checkClassForDuplicateDeclarations(node: ClassLikeDeclaration) { - const getter = 1, setter = 2, property = getter | setter; + const enum Accessor { + Getter = 1, + Setter = 2, + Property = Getter | Setter + } - const instanceNames: Map = {}; - const staticNames: Map = {}; + const instanceNames = createMap(); + const staticNames = createMap(); for (const member of node.members) { if (member.kind === SyntaxKind.Constructor) { for (const param of (member as ConstructorDeclaration).parameters) { if (isParameterPropertyDeclaration(param)) { - addName(instanceNames, param.name, (param.name as Identifier).text, property); + addName(instanceNames, param.name, (param.name as Identifier).text, Accessor.Property); } } } @@ -13687,24 +13873,24 @@ namespace ts { if (memberName) { switch (member.kind) { case SyntaxKind.GetAccessor: - addName(names, member.name, memberName, getter); + addName(names, member.name, memberName, Accessor.Getter); break; case SyntaxKind.SetAccessor: - addName(names, member.name, memberName, setter); + addName(names, member.name, memberName, Accessor.Setter); break; case SyntaxKind.PropertyDeclaration: - addName(names, member.name, memberName, property); + addName(names, member.name, memberName, Accessor.Property); break; } } } } - function addName(names: Map, location: Node, name: string, meaning: number) { - if (hasProperty(names, name)) { - const prev = names[name]; + function addName(names: Map, location: Node, name: string, meaning: Accessor) { + const prev = names[name]; + if (prev) { if (prev & meaning) { error(location, Diagnostics.Duplicate_identifier_0, getTextOfNode(location)); } @@ -13719,7 +13905,7 @@ namespace ts { } function checkObjectTypeForDuplicateDeclarations(node: TypeLiteralNode | InterfaceDeclaration) { - const names: Map = {}; + const names = createMap(); for (const member of node.members) { if (member.kind == SyntaxKind.PropertySignature) { let memberName: string; @@ -13733,7 +13919,7 @@ namespace ts { continue; } - if (hasProperty(names, memberName)) { + if (names[memberName]) { error(member.symbol.valueDeclaration.name, Diagnostics.Duplicate_identifier_0, memberName); error(member.name, Diagnostics.Duplicate_identifier_0, memberName); } @@ -14945,22 +15131,20 @@ namespace ts { function checkUnusedLocalsAndParameters(node: Node): void { if (node.parent.kind !== SyntaxKind.InterfaceDeclaration && noUnusedIdentifiers && !isInAmbientContext(node)) { for (const key in node.locals) { - if (hasProperty(node.locals, key)) { - const local = node.locals[key]; - if (!local.isReferenced) { - if (local.valueDeclaration && local.valueDeclaration.kind === SyntaxKind.Parameter) { - const parameter = local.valueDeclaration; - if (compilerOptions.noUnusedParameters && - !isParameterPropertyDeclaration(parameter) && - !parameterIsThisKeyword(parameter) && - !parameterNameStartsWithUnderscore(parameter)) { - error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name); - } - } - else if (compilerOptions.noUnusedLocals) { - forEach(local.declarations, d => error(d.name || d, Diagnostics._0_is_declared_but_never_used, local.name)); + const local = node.locals[key]; + if (!local.isReferenced) { + if (local.valueDeclaration && local.valueDeclaration.kind === SyntaxKind.Parameter) { + const parameter = local.valueDeclaration; + if (compilerOptions.noUnusedParameters && + !isParameterPropertyDeclaration(parameter) && + !parameterIsThisKeyword(parameter) && + !parameterNameStartsWithUnderscore(parameter)) { + error(local.valueDeclaration.name, Diagnostics._0_is_declared_but_never_used, local.name); } } + else if (compilerOptions.noUnusedLocals) { + forEach(local.declarations, d => error(d.name || d, Diagnostics._0_is_declared_but_never_used, local.name)); + } } } } @@ -14986,7 +15170,7 @@ namespace ts { else if (member.kind === SyntaxKind.Constructor) { for (const parameter of (member).parameters) { if (!parameter.symbol.isReferenced && parameter.flags & NodeFlags.Private) { - error(parameter.name, Diagnostics._0_is_declared_but_never_used, parameter.symbol.name); + error(parameter.name, Diagnostics.Property_0_is_declared_but_never_used, parameter.symbol.name); } } } @@ -15017,13 +15201,11 @@ namespace ts { function checkUnusedModuleMembers(node: ModuleDeclaration | SourceFile): void { if (compilerOptions.noUnusedLocals && !isInAmbientContext(node)) { for (const key in node.locals) { - if (hasProperty(node.locals, key)) { - const local = node.locals[key]; - if (!local.isReferenced && !local.exportSymbol) { - for (const declaration of local.declarations) { - if (!isAmbientModule(declaration)) { - error(declaration.name, Diagnostics._0_is_declared_but_never_used, local.name); - } + const local = node.locals[key]; + if (!local.isReferenced && !local.exportSymbol) { + for (const declaration of local.declarations) { + if (!isAmbientModule(declaration)) { + error(declaration.name, Diagnostics._0_is_declared_but_never_used, local.name); } } } @@ -16032,7 +16214,7 @@ namespace ts { else { const identifierName = (catchClause.variableDeclaration.name).text; const locals = catchClause.block.locals; - if (locals && hasProperty(locals, identifierName)) { + if (locals) { const localSymbol = locals[identifierName]; if (localSymbol && (localSymbol.flags & SymbolFlags.BlockScopedVariable) !== 0) { grammarErrorOnNode(localSymbol.valueDeclaration, Diagnostics.Cannot_redeclare_identifier_0_in_catch_clause, identifierName); @@ -16269,7 +16451,7 @@ namespace ts { const implementedTypeNodes = getClassImplementsHeritageClauseElements(node); if (implementedTypeNodes) { for (const typeRefNode of implementedTypeNodes) { - if (!isSupportedExpressionWithTypeArguments(typeRefNode)) { + if (!isEntityNameExpression(typeRefNode.expression)) { error(typeRefNode.expression, Diagnostics.A_class_can_only_implement_an_identifier_Slashqualified_name_with_optional_type_arguments); } checkTypeReferenceNode(typeRefNode); @@ -16453,18 +16635,18 @@ namespace ts { return true; } - const seen: Map<{ prop: Symbol; containingType: Type }> = {}; + const seen = createMap<{ prop: Symbol; containingType: Type }>(); forEach(resolveDeclaredMembers(type).declaredProperties, p => { seen[p.name] = { prop: p, containingType: type }; }); let ok = true; for (const base of baseTypes) { const properties = getPropertiesOfObjectType(getTypeWithThisArgument(base, type.thisType)); for (const prop of properties) { - if (!hasProperty(seen, prop.name)) { + const existing = seen[prop.name]; + if (!existing) { seen[prop.name] = { prop: prop, containingType: base }; } else { - const existing = seen[prop.name]; const isInheritedProperty = existing.containingType !== type; if (isInheritedProperty && !isPropertyIdenticalTo(existing.prop, prop)) { ok = false; @@ -16511,7 +16693,7 @@ namespace ts { checkObjectTypeForDuplicateDeclarations(node); } forEach(getInterfaceBaseTypeNodes(node), heritageElement => { - if (!isSupportedExpressionWithTypeArguments(heritageElement)) { + if (!isEntityNameExpression(heritageElement.expression)) { error(heritageElement.expression, Diagnostics.An_interface_can_only_extend_an_identifier_Slashqualified_name_with_optional_type_arguments); } checkTypeReferenceNode(heritageElement); @@ -16976,20 +17158,21 @@ namespace ts { } } - function getFirstIdentifier(node: EntityName | Expression): Identifier { - while (true) { - if (node.kind === SyntaxKind.QualifiedName) { - node = (node).left; - } - else if (node.kind === SyntaxKind.PropertyAccessExpression) { - node = (node).expression; - } - else { - break; - } + function getFirstIdentifier(node: EntityNameOrEntityNameExpression): Identifier { + switch (node.kind) { + case SyntaxKind.Identifier: + return node; + case SyntaxKind.QualifiedName: + do { + node = (node).left; + } while (node.kind !== SyntaxKind.Identifier); + return node; + case SyntaxKind.PropertyAccessExpression: + do { + node = (node).expression; + } while (node.kind !== SyntaxKind.Identifier); + return node; } - Debug.assert(node.kind === SyntaxKind.Identifier); - return node; } function checkExternalImportOrExportDeclaration(node: ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration): boolean { @@ -17418,11 +17601,10 @@ namespace ts { } function checkSourceFile(node: SourceFile) { - const start = performance.mark(); - + performance.mark("beforeCheck"); checkSourceFileWorker(node); - - performance.measure("Check", start); + performance.mark("afterCheck"); + performance.measure("Check", "beforeCheck", "afterCheck"); } // Fully type check a source file and collect the relevant diagnostics. @@ -17522,7 +17704,7 @@ namespace ts { } function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { - const symbols: SymbolTable = {}; + const symbols = createMap(); let memberFlags: NodeFlags = 0; if (isInsideWithStatementBody(location)) { @@ -17600,7 +17782,7 @@ namespace ts { // We will copy all symbol regardless of its reserved name because // symbolsToArray will check whether the key is a reserved name and // it will not copy symbol with reserved name to the array - if (!hasProperty(symbols, id)) { + if (!symbols[id]) { symbols[id] = symbol; } } @@ -17688,7 +17870,7 @@ namespace ts { return getLeftSideOfImportEqualsOrExportAssignment(node) !== undefined; } - function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol { + function getSymbolOfEntityNameOrPropertyAccessExpression(entityName: EntityName | PropertyAccessExpression): Symbol | undefined { if (isDeclarationName(entityName)) { return getSymbolOfNode(entityName.parent); } @@ -17707,22 +17889,20 @@ namespace ts { } } - if (entityName.parent.kind === SyntaxKind.ExportAssignment) { - return resolveEntityName(entityName, + if (entityName.parent.kind === SyntaxKind.ExportAssignment && isEntityNameExpression(entityName)) { + return resolveEntityName(entityName, /*all meanings*/ SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias); } - if (entityName.kind !== SyntaxKind.PropertyAccessExpression) { - if (isInRightSideOfImportOrExportAssignment(entityName)) { - // Since we already checked for ExportAssignment, this really could only be an Import - const importEqualsDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration); - Debug.assert(importEqualsDeclaration !== undefined); - return getSymbolOfPartOfRightHandSideOfImportEquals(entityName, importEqualsDeclaration, /*dontResolveAlias*/ true); - } + if (entityName.kind !== SyntaxKind.PropertyAccessExpression && isInRightSideOfImportOrExportAssignment(entityName)) { + // Since we already checked for ExportAssignment, this really could only be an Import + const importEqualsDeclaration = getAncestor(entityName, SyntaxKind.ImportEqualsDeclaration); + Debug.assert(importEqualsDeclaration !== undefined); + return getSymbolOfPartOfRightHandSideOfImportEquals(entityName, importEqualsDeclaration, /*dontResolveAlias*/ true); } if (isRightSideOfQualifiedNameOrPropertyAccess(entityName)) { - entityName = entityName.parent; + entityName = entityName.parent; } if (isHeritageClauseElementIdentifier(entityName)) { @@ -18021,7 +18201,7 @@ namespace ts { const propsByName = createSymbolTable(getPropertiesOfType(type)); if (getSignaturesOfType(type, SignatureKind.Call).length || getSignaturesOfType(type, SignatureKind.Construct).length) { forEach(getPropertiesOfType(globalFunctionType), p => { - if (!hasProperty(propsByName, p.name)) { + if (!propsByName[p.name]) { propsByName[p.name] = p; } }); @@ -18078,7 +18258,7 @@ namespace ts { // otherwise - check if at least one export is value symbolLinks.exportsSomeValue = hasExportAssignment ? !!(moduleSymbol.flags & SymbolFlags.Value) - : forEachValue(getExportsOfModule(moduleSymbol), isValue); + : forEachProperty(getExportsOfModule(moduleSymbol), isValue); } return symbolLinks.exportsSomeValue; @@ -18369,7 +18549,7 @@ namespace ts { } function hasGlobalName(name: string): boolean { - return hasProperty(globals, name); + return !!globals[name]; } function getReferencedValueSymbol(reference: Identifier): Symbol { @@ -18393,9 +18573,6 @@ namespace ts { // populate reverse mapping: file path -> type reference directive that was resolved to this file fileToDirective = createFileMap(); for (const key in resolvedTypeReferenceDirectives) { - if (!hasProperty(resolvedTypeReferenceDirectives, key)) { - continue; - } const resolvedDirective = resolvedTypeReferenceDirectives[key]; if (!resolvedDirective) { continue; @@ -18435,7 +18612,7 @@ namespace ts { }; // defined here to avoid outer scope pollution - function getTypeReferenceDirectivesForEntityName(node: EntityName | PropertyAccessExpression): string[] { + function getTypeReferenceDirectivesForEntityName(node: EntityNameOrEntityNameExpression): string[] { // program does not have any files with type reference directives - bail out if (!fileToDirective) { return undefined; @@ -19184,7 +19361,7 @@ namespace ts { } function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) { - const seen: Map = {}; + const seen = createMap(); const Property = 1; const GetAccessor = 2; const SetAccessor = 4; @@ -19246,7 +19423,7 @@ namespace ts { continue; } - if (!hasProperty(seen, effectiveName)) { + if (!seen[effectiveName]) { seen[effectiveName] = currentKind; } else { @@ -19270,7 +19447,7 @@ namespace ts { } function checkGrammarJsxElement(node: JsxOpeningLikeElement) { - const seen: Map = {}; + const seen = createMap(); for (const attr of node.attributes) { if (attr.kind === SyntaxKind.JsxSpreadAttribute) { continue; @@ -19278,7 +19455,7 @@ namespace ts { const jsxAttr = (attr); const name = jsxAttr.name; - if (!hasProperty(seen, name.text)) { + if (!seen[name.text]) { seen[name.text] = true; } else { @@ -19673,7 +19850,7 @@ namespace ts { } function checkGrammarTopLevelElementForRequiredDeclareModifier(node: Node): boolean { - // A declare modifier is required for any top level .d.ts declaration except export=, export default, + // A declare modifier is required for any top level .d.ts declaration except export=, export default, export as namespace // interfaces and imports categories: // // DeclarationElement: @@ -19691,6 +19868,7 @@ namespace ts { node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.ExportDeclaration || node.kind === SyntaxKind.ExportAssignment || + node.kind === SyntaxKind.NamespaceExportDeclaration || (node.flags & NodeFlags.Ambient) || (node.flags & (NodeFlags.Export | NodeFlags.Default))) { diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 7e2c6eb8d33..616bf5e70c0 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -61,10 +61,10 @@ namespace ts { }, { name: "jsx", - type: { + type: createMap({ "preserve": JsxEmit.Preserve, "react": JsxEmit.React - }, + }), paramType: Diagnostics.KIND, description: Diagnostics.Specify_JSX_code_generation_Colon_preserve_or_react, }, @@ -91,7 +91,7 @@ namespace ts { { name: "module", shortName: "m", - type: { + type: createMap({ "none": ModuleKind.None, "commonjs": ModuleKind.CommonJS, "amd": ModuleKind.AMD, @@ -99,16 +99,16 @@ namespace ts { "umd": ModuleKind.UMD, "es6": ModuleKind.ES6, "es2015": ModuleKind.ES2015, - }, + }), description: Diagnostics.Specify_module_code_generation_Colon_commonjs_amd_system_umd_or_es2015, paramType: Diagnostics.KIND, }, { name: "newLine", - type: { + type: createMap({ "crlf": NewLineKind.CarriageReturnLineFeed, "lf": NewLineKind.LineFeed - }, + }), description: Diagnostics.Specify_the_end_of_line_sequence_to_be_used_when_emitting_files_Colon_CRLF_dos_or_LF_unix, paramType: Diagnostics.NEWLINE, }, @@ -250,12 +250,12 @@ namespace ts { { name: "target", shortName: "t", - type: { + type: createMap({ "es3": ScriptTarget.ES3, "es5": ScriptTarget.ES5, "es6": ScriptTarget.ES6, "es2015": ScriptTarget.ES2015, - }, + }), description: Diagnostics.Specify_ECMAScript_target_version_Colon_ES3_default_ES5_or_ES2015, paramType: Diagnostics.VERSION, }, @@ -284,10 +284,10 @@ namespace ts { }, { name: "moduleResolution", - type: { + type: createMap({ "node": ModuleResolutionKind.NodeJs, "classic": ModuleResolutionKind.Classic, - }, + }), description: Diagnostics.Specify_module_resolution_strategy_Colon_node_Node_js_or_classic_TypeScript_pre_1_6, }, { @@ -392,7 +392,7 @@ namespace ts { type: "list", element: { name: "lib", - type: { + type: createMap({ // JavaScript only "es5": "lib.es5.d.ts", "es6": "lib.es2015.d.ts", @@ -417,7 +417,7 @@ namespace ts { "es2016.array.include": "lib.es2016.array.include.d.ts", "es2017.object": "lib.es2017.object.d.ts", "es2017.sharedmemory": "lib.es2017.sharedmemory.d.ts" - }, + }), }, description: Diagnostics.Specify_library_files_to_be_included_in_the_compilation_Colon }, @@ -470,8 +470,8 @@ namespace ts { return optionNameMapCache; } - const optionNameMap: Map = {}; - const shortOptionNames: Map = {}; + const optionNameMap = createMap(); + const shortOptionNames = createMap(); forEach(optionDeclarations, option => { optionNameMap[option.name.toLowerCase()] = option; if (option.shortName) { @@ -486,10 +486,9 @@ namespace ts { /* @internal */ export function createCompilerDiagnosticForInvalidCustomType(opt: CommandLineOptionOfCustomType): Diagnostic { const namesOfType: string[] = []; - forEachKey(opt.type, key => { + for (const key in opt.type) { namesOfType.push(` '${key}'`); - }); - + } return createCompilerDiagnostic(Diagnostics.Argument_for_0_option_must_be_Colon_1, `--${opt.name}`, namesOfType); } @@ -497,7 +496,7 @@ namespace ts { export function parseCustomTypeOption(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = trimString((value || "")).toLowerCase(); const map = opt.type; - if (hasProperty(map, key)) { + if (key in map) { return map[key]; } else { @@ -551,11 +550,11 @@ namespace ts { s = s.slice(s.charCodeAt(1) === CharacterCodes.minus ? 2 : 1).toLowerCase(); // Try to translate short option names to their full equivalents. - if (hasProperty(shortOptionNames, s)) { + if (s in shortOptionNames) { s = shortOptionNames[s]; } - if (hasProperty(optionNameMap, s)) { + if (s in optionNameMap) { const opt = optionNameMap[s]; if (opt.isTSConfigOnly) { @@ -811,7 +810,7 @@ namespace ts { const optionNameMap = arrayToMap(optionDeclarations, opt => opt.name); for (const id in jsonOptions) { - if (hasProperty(optionNameMap, id)) { + if (id in optionNameMap) { const opt = optionNameMap[id]; defaultOptions[opt.name] = convertJsonOption(opt, jsonOptions[id], basePath, errors); } @@ -848,7 +847,7 @@ namespace ts { function convertJsonOptionOfCustomType(opt: CommandLineOptionOfCustomType, value: string, errors: Diagnostic[]) { const key = value.toLowerCase(); - if (hasProperty(opt.type, key)) { + if (key in opt.type) { return opt.type[key]; } else { @@ -958,12 +957,12 @@ namespace ts { // Literal file names (provided via the "files" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map later when when including // wildcard paths. - const literalFileMap: Map = {}; + const literalFileMap = createMap(); // Wildcard paths (provided via the "includes" array in tsconfig.json) are stored in a // file map with a possibly case insensitive key. We use this map to store paths matched // via wildcard, and to handle extension priority. - const wildcardFileMap: Map = {}; + const wildcardFileMap = createMap(); if (include) { include = validateSpecs(include, errors, /*allowTrailingRecursion*/ false); @@ -1011,7 +1010,7 @@ namespace ts { removeWildcardFilesWithLowerPriorityExtension(file, wildcardFileMap, supportedExtensions, keyMapper); const key = keyMapper(file); - if (!hasProperty(literalFileMap, key) && !hasProperty(wildcardFileMap, key)) { + if (!(key in literalFileMap) && !(key in wildcardFileMap)) { wildcardFileMap[key] = file; } } @@ -1063,7 +1062,7 @@ namespace ts { // /a/b/a?z - Watch /a/b directly to catch any new file matching a?z const rawExcludeRegex = getRegularExpressionForWildcard(exclude, path, "exclude"); const excludeRegex = rawExcludeRegex && new RegExp(rawExcludeRegex, useCaseSensitiveFileNames ? "" : "i"); - const wildcardDirectories: Map = {}; + const wildcardDirectories = createMap(); if (include !== undefined) { const recursiveKeys: string[] = []; for (const file of include) { @@ -1076,7 +1075,7 @@ namespace ts { if (match) { const key = useCaseSensitiveFileNames ? match[0] : match[0].toLowerCase(); const flags = watchRecursivePattern.test(name) ? WatchDirectoryFlags.Recursive : WatchDirectoryFlags.None; - const existingFlags = getProperty(wildcardDirectories, key); + const existingFlags = wildcardDirectories[key]; if (existingFlags === undefined || existingFlags < flags) { wildcardDirectories[key] = flags; if (flags === WatchDirectoryFlags.Recursive) { @@ -1088,11 +1087,9 @@ namespace ts { // Remove any subpaths under an existing recursively watched directory. for (const key in wildcardDirectories) { - if (hasProperty(wildcardDirectories, key)) { - for (const recursiveKey of recursiveKeys) { - if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { - delete wildcardDirectories[key]; - } + for (const recursiveKey of recursiveKeys) { + if (key !== recursiveKey && containsPath(recursiveKey, key, path, !useCaseSensitiveFileNames)) { + delete wildcardDirectories[key]; } } } @@ -1115,7 +1112,7 @@ namespace ts { for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) { const higherPriorityExtension = extensions[i]; const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension)); - if (hasProperty(literalFiles, higherPriorityPath) || hasProperty(wildcardFiles, higherPriorityPath)) { + if (higherPriorityPath in literalFiles || higherPriorityPath in wildcardFiles) { return true; } } diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 709a331e022..40341929c02 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -19,8 +19,28 @@ namespace ts { True = -1 } + const createObject = Object.create; + + export function createMap(template?: MapLike): Map { + const map: Map = createObject(null); // tslint:disable-line:no-null-keyword + + // Using 'delete' on an object causes V8 to put the object in dictionary mode. + // This disables creation of hidden classes, which are expensive when an object is + // constantly changing shape. + map["__"] = undefined; + delete map["__"]; + + // Copies keys/values from template. Note that for..in will not throw if + // template is undefined, and instead will just exit the loop. + for (const key in template) if (hasOwnProperty.call(template, key)) { + map[key] = template[key]; + } + + return map; + } + export function createFileMap(keyMapper?: (key: string) => string): FileMap { - let files: Map = {}; + let files = createMap(); return { get, set, @@ -46,7 +66,7 @@ namespace ts { } function contains(path: Path) { - return hasProperty(files, toKey(path)); + return toKey(path) in files; } function remove(path: Path) { @@ -55,7 +75,7 @@ namespace ts { } function clear() { - files = {}; + files = createMap(); } function toKey(path: Path): string { @@ -81,7 +101,7 @@ namespace ts { * returns a truthy value, then returns that value. * If no such value is found, the callback is applied to each element of array and undefined is returned. */ - export function forEach(array: T[], callback: (element: T, index: number) => U): U { + export function forEach(array: T[] | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { if (array) { for (let i = 0, len = array.length; i < len; i++) { const result = callback(array[i], i); @@ -93,6 +113,17 @@ namespace ts { return undefined; } + /** Like `forEach`, but assumes existence of array and fails if no truthy value is found. */ + export function find(array: T[], callback: (element: T, index: number) => U | undefined): U { + for (let i = 0, len = array.length; i < len; i++) { + const result = callback(array[i], i); + if (result) { + return result; + } + } + Debug.fail(); + } + export function contains(array: T[], value: T): boolean { if (array) { for (const v of array) { @@ -136,17 +167,44 @@ namespace ts { return count; } + /** + * Filters an array by a predicate function. Returns the same array instance if the predicate is + * true for all elements, otherwise returns a new array instance containing the filtered subset. + */ export function filter(array: T[], f: (x: T) => boolean): T[] { - let result: T[]; if (array) { - result = []; - for (const item of array) { - if (f(item)) { - result.push(item); + const len = array.length; + let i = 0; + while (i < len && f(array[i])) i++; + if (i < len) { + const result = array.slice(0, i); + i++; + while (i < len) { + const item = array[i]; + if (f(item)) { + result.push(item); + } + i++; } + return result; } } - return result; + return array; + } + + export function removeWhere(array: T[], f: (x: T) => boolean): boolean { + let outIndex = 0; + for (const item of array) { + if (!f(item)) { + array[outIndex] = item; + outIndex++; + } + } + if (outIndex !== array.length) { + array.length = outIndex; + return true; + } + return false; } export function filterMutate(array: T[], f: (x: T) => boolean): void { @@ -311,78 +369,142 @@ namespace ts { const hasOwnProperty = Object.prototype.hasOwnProperty; - export function hasProperty(map: Map, key: string): boolean { + /** + * Indicates whether a map-like contains an own property with the specified key. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * the 'in' operator. + * + * @param map A map-like. + * @param key A property key. + */ + export function hasProperty(map: MapLike, key: string): boolean { return hasOwnProperty.call(map, key); } - export function getKeys(map: Map): string[] { + /** + * Gets the value of an owned property in a map-like. + * + * NOTE: This is intended for use only with MapLike objects. For Map objects, use + * an indexer. + * + * @param map A map-like. + * @param key A property key. + */ + export function getProperty(map: MapLike, key: string): T | undefined { + return hasOwnProperty.call(map, key) ? map[key] : undefined; + } + + /** + * Gets the owned, enumerable property keys of a map-like. + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * Object.keys instead as it offers better performance. + * + * @param map A map-like. + */ + export function getOwnKeys(map: MapLike): string[] { const keys: string[] = []; - for (const key in map) { + for (const key in map) if (hasOwnProperty.call(map, key)) { keys.push(key); } return keys; } - export function getProperty(map: Map, key: string): T { - return hasOwnProperty.call(map, key) ? map[key] : undefined; + /** + * Enumerates the properties of a Map, invoking a callback and returning the first truthy result. + * + * @param map A map for which properties should be enumerated. + * @param callback A callback to invoke for each property. + */ + export function forEachProperty(map: Map, callback: (value: T, key: string) => U): U { + let result: U; + for (const key in map) { + if (result = callback(map[key], key)) break; + } + return result; } - export function isEmpty(map: Map) { - for (const id in map) { - if (hasProperty(map, id)) { - return false; - } + /** + * Returns true if a Map has some matching property. + * + * @param map A map whose properties should be tested. + * @param predicate An optional callback used to test each property. + */ + export function someProperties(map: Map, predicate?: (value: T, key: string) => boolean) { + for (const key in map) { + if (!predicate || predicate(map[key], key)) return true; + } + return false; + } + + /** + * Performs a shallow copy of the properties from a source Map to a target MapLike + * + * @param source A map from which properties should be copied. + * @param target A map to which properties should be copied. + */ + export function copyProperties(source: Map, target: MapLike): void { + for (const key in source) { + target[key] = source[key]; + } + } + + /** + * Reduce the properties of a map. + * + * NOTE: This is intended for use with Map objects. For MapLike objects, use + * reduceOwnProperties instead as it offers better runtime safety. + * + * @param map The map to reduce + * @param callback An aggregation function that is called for each entry in the map + * @param initial The initial value for the reduction. + */ + export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + let result = initial; + for (const key in map) { + result = callback(result, map[key], String(key)); + } + return result; + } + + /** + * Reduce the properties defined on a map-like (but not from its prototype chain). + * + * NOTE: This is intended for use with MapLike objects. For Map objects, use + * reduceProperties instead as it offers better performance. + * + * @param map The map-like to reduce + * @param callback An aggregation function that is called for each entry in the map + * @param initial The initial value for the reduction. + */ + export function reduceOwnProperties(map: MapLike, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { + let result = initial; + for (const key in map) if (hasOwnProperty.call(map, key)) { + result = callback(result, map[key], String(key)); + } + return result; + } + + /** + * Performs a shallow equality comparison of the contents of two map-likes. + * + * @param left A map-like whose properties should be compared. + * @param right A map-like whose properties should be compared. + */ + export function equalOwnProperties(left: MapLike, right: MapLike, equalityComparer?: (left: T, right: T) => boolean) { + if (left === right) return true; + if (!left || !right) return false; + for (const key in left) if (hasOwnProperty.call(left, key)) { + if (!hasOwnProperty.call(right, key) === undefined) return false; + if (equalityComparer ? !equalityComparer(left[key], right[key]) : left[key] !== right[key]) return false; + } + for (const key in right) if (hasOwnProperty.call(right, key)) { + if (!hasOwnProperty.call(left, key)) return false; } return true; } - export function clone(object: T): T { - const result: any = {}; - for (const id in object) { - result[id] = (object)[id]; - } - return result; - } - - export function extend, T2 extends Map<{}>>(first: T1 , second: T2): T1 & T2 { - const result: T1 & T2 = {}; - for (const id in first) { - (result as any)[id] = first[id]; - } - for (const id in second) { - if (!hasProperty(result, id)) { - (result as any)[id] = second[id]; - } - } - return result; - } - - export function forEachValue(map: Map, callback: (value: T) => U): U { - let result: U; - for (const id in map) { - if (result = callback(map[id])) break; - } - return result; - } - - export function forEachKey(map: Map, callback: (key: string) => U): U { - let result: U; - for (const id in map) { - if (result = callback(id)) break; - } - return result; - } - - export function lookUp(map: Map, key: string): T { - return hasProperty(map, key) ? map[key] : undefined; - } - - export function copyMap(source: Map, target: Map): void { - for (const p in source) { - target[p] = source[p]; - } - } - /** * Creates a map from the elements of an array. * @@ -393,33 +515,40 @@ namespace ts { * the same key with the given 'makeKey' function, then the element with the higher * index in the array will be the one associated with the produced key. */ - export function arrayToMap(array: T[], makeKey: (value: T) => string): Map { - const result: Map = {}; - - forEach(array, value => { - result[makeKey(value)] = value; - }); - + export function arrayToMap(array: T[], makeKey: (value: T) => string): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue: (value: T) => U): Map; + export function arrayToMap(array: T[], makeKey: (value: T) => string, makeValue?: (value: T) => U): Map { + const result = createMap(); + for (const value of array) { + result[makeKey(value)] = makeValue ? makeValue(value) : value; + } return result; } - /** - * Reduce the properties of a map. - * - * @param map The map to reduce - * @param callback An aggregation function that is called for each entry in the map - * @param initial The initial value for the reduction. - */ - export function reduceProperties(map: Map, callback: (aggregate: U, value: T, key: string) => U, initial: U): U { - let result = initial; - if (map) { - for (const key in map) { - if (hasProperty(map, key)) { - result = callback(result, map[key], String(key)); - } + export function cloneMap(map: Map) { + const clone = createMap(); + copyProperties(map, clone); + return clone; + } + + export function clone(object: T): T { + const result: any = {}; + for (const id in object) { + if (hasOwnProperty.call(object, id)) { + result[id] = (object)[id]; } } + return result; + } + export function extend(first: T1 , second: T2): T1 & T2 { + const result: T1 & T2 = {}; + for (const id in second) if (hasOwnProperty.call(second, id)) { + (result as any)[id] = (second as any)[id]; + } + for (const id in first) if (hasOwnProperty.call(first, id)) { + (result as any)[id] = (first as any)[id]; + } return result; } @@ -450,9 +579,7 @@ namespace ts { export let localizedDiagnosticMessages: Map = undefined; export function getLocaleSpecificMessage(message: DiagnosticMessage) { - return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] - ? localizedDiagnosticMessages[message.key] - : message.message; + return localizedDiagnosticMessages && localizedDiagnosticMessages[message.key] || message.message; } export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage, ...args: any[]): Diagnostic; @@ -941,7 +1068,7 @@ namespace ts { * [^./] # matches everything up to the first . character (excluding directory seperators) * (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension */ - const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; + const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*"; const singleAsteriskRegexFragmentOther = "[^/]*"; export function getRegularExpressionForWildcard(specs: string[], basePath: string, usage: "files" | "directories" | "exclude") { diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index d93a8a0aed0..3642ee7f10a 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -157,9 +157,7 @@ namespace ts { if (usedTypeDirectiveReferences) { for (const directive in usedTypeDirectiveReferences) { - if (hasProperty(usedTypeDirectiveReferences, directive)) { - referencesOutput += `/// ${newLine}`; - } + referencesOutput += `/// ${newLine}`; } } @@ -269,10 +267,10 @@ namespace ts { } if (!usedTypeDirectiveReferences) { - usedTypeDirectiveReferences = {}; + usedTypeDirectiveReferences = createMap(); } for (const directive of typeReferenceDirectives) { - if (!hasProperty(usedTypeDirectiveReferences, directive)) { + if (!(directive in usedTypeDirectiveReferences)) { usedTypeDirectiveReferences[directive] = directive; } } @@ -441,7 +439,7 @@ namespace ts { } } - function emitEntityName(entityName: EntityName | PropertyAccessExpression) { + function emitEntityName(entityName: EntityNameOrEntityNameExpression) { const visibilityResult = resolver.isEntityNameVisible(entityName, // Aliases can be written asynchronously so use correct enclosing declaration entityName.parent.kind === SyntaxKind.ImportEqualsDeclaration ? entityName.parent : enclosingDeclaration); @@ -452,9 +450,9 @@ namespace ts { } function emitExpressionWithTypeArguments(node: ExpressionWithTypeArguments) { - if (isSupportedExpressionWithTypeArguments(node)) { + if (isEntityNameExpression(node.expression)) { Debug.assert(node.expression.kind === SyntaxKind.Identifier || node.expression.kind === SyntaxKind.PropertyAccessExpression); - emitEntityName(node.expression); + emitEntityName(node.expression); if (node.typeArguments) { write("<"); emitCommaList(node.typeArguments, emitType); @@ -537,14 +535,14 @@ namespace ts { // do not need to keep track of created temp names. function getExportDefaultTempVariableName(): string { const baseName = "_default"; - if (!hasProperty(currentIdentifiers, baseName)) { + if (!(baseName in currentIdentifiers)) { return baseName; } let count = 0; while (true) { count++; const name = baseName + "_" + count; - if (!hasProperty(currentIdentifiers, name)) { + if (!(name in currentIdentifiers)) { return name; } } @@ -1019,7 +1017,7 @@ namespace ts { } function emitTypeOfTypeReference(node: ExpressionWithTypeArguments) { - if (isSupportedExpressionWithTypeArguments(node)) { + if (isEntityNameExpression(node.expression)) { emitTypeWithNewGetSymbolAccessibilityDiagnostic(node, getHeritageClauseVisibilityError); } else if (!isImplementsList && node.expression.kind === SyntaxKind.NullKeyword) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8126d5c605e..c6a3698ea16 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2231,7 +2231,7 @@ "category": "Error", "code": 4082 }, - "Conflicting library definitions for '{0}' found at '{1}' and '{2}'. Copy the correct file to the 'typings' folder to resolve this conflict.": { + "Conflicting definitions for '{0}' found at '{1}' and '{2}'. Consider installing a specific version of this library to resolve the conflict.": { "category": "Message", "code": 4090 }, @@ -2828,6 +2828,10 @@ "category": "Message", "code": 6137 }, + "Property '{0}' is declared but never used.": { + "category": "Error", + "code": 6138 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 5fe5027978c..869f2decce7 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -24,7 +24,7 @@ namespace ts { Return = 1 << 3 } - const entities: Map = { + const entities = createMap({ "quot": 0x0022, "amp": 0x0026, "apos": 0x0027, @@ -278,7 +278,7 @@ namespace ts { "clubs": 0x2663, "hearts": 0x2665, "diams": 0x2666 - }; + }); // Flags enum to track count of temp variables and a few dedicated names const enum TempFlags { @@ -407,7 +407,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function isUniqueLocalName(name: string, container: Node): boolean { for (let node = container; isNodeDescendentOf(node, container); node = node.nextContainer) { - if (node.locals && hasProperty(node.locals, name)) { + if (node.locals && name in node.locals) { // We conservatively include alias symbols to cover cases where they're emitted as locals if (node.locals[name].flags & (SymbolFlags.Value | SymbolFlags.ExportValue | SymbolFlags.Alias)) { return false; @@ -489,13 +489,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function setLabeledJump(state: ConvertedLoopState, isBreak: boolean, labelText: string, labelMarker: string): void { if (isBreak) { if (!state.labeledNonLocalBreaks) { - state.labeledNonLocalBreaks = {}; + state.labeledNonLocalBreaks = createMap(); } state.labeledNonLocalBreaks[labelText] = labelMarker; } else { if (!state.labeledNonLocalContinues) { - state.labeledNonLocalContinues = {}; + state.labeledNonLocalContinues = createMap(); } state.labeledNonLocalContinues[labelText] = labelMarker; } @@ -577,27 +577,27 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge const setSourceMapWriterEmit = compilerOptions.sourceMap || compilerOptions.inlineSourceMap ? changeSourceMapEmit : function (writer: SourceMapWriter) { }; - const moduleEmitDelegates: Map<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = { + const moduleEmitDelegates = createMap<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void>({ [ModuleKind.ES6]: emitES6Module, [ModuleKind.AMD]: emitAMDModule, [ModuleKind.System]: emitSystemModule, [ModuleKind.UMD]: emitUMDModule, [ModuleKind.CommonJS]: emitCommonJSModule, - }; + }); - const bundleEmitDelegates: Map<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void> = { + const bundleEmitDelegates = createMap<(node: SourceFile, emitRelativePathAsModuleName?: boolean) => void>({ [ModuleKind.ES6]() {}, [ModuleKind.AMD]: emitAMDModule, [ModuleKind.System]: emitSystemModule, [ModuleKind.UMD]() {}, [ModuleKind.CommonJS]() {}, - }; + }); return doEmit; function doEmit(jsFilePath: string, sourceMapFilePath: string, sourceFiles: SourceFile[], isBundledEmit: boolean) { sourceMap.initialize(jsFilePath, sourceMapFilePath, sourceFiles, isBundledEmit); - generatedNameSet = {}; + generatedNameSet = createMap(); nodeToGeneratedName = []; decoratedClassAliases = []; isOwnFileEmit = !isBundledEmit; @@ -669,8 +669,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge function isUniqueName(name: string): boolean { return !resolver.hasGlobalName(name) && - !hasProperty(currentFileIdentifiers, name) && - !hasProperty(generatedNameSet, name); + !(name in currentFileIdentifiers) && + !(name in generatedNameSet); } // Return the next available name in the pattern _a ... _z, _0, _1, ... @@ -2646,7 +2646,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return false; } - return !exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, (node).text); + return !exportEquals && exportSpecifiers && (node).text in exportSpecifiers; } function emitPrefixUnaryExpression(node: PrefixUnaryExpression) { @@ -3257,13 +3257,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge // Don't initialize seen unless we have at least one element. // Emit a comma to separate for all but the first element. if (!seen) { - seen = {}; + seen = createMap(); } else { write(", "); } - if (!hasProperty(seen, id.text)) { + if (!(id.text in seen)) { emit(id); seen[id.text] = id.text; } @@ -3856,7 +3856,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge if (convertedLoopState) { if (!convertedLoopState.labels) { - convertedLoopState.labels = {}; + convertedLoopState.labels = createMap(); } convertedLoopState.labels[node.label.text] = node.label.text; } @@ -3970,7 +3970,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge return; } - if (!exportEquals && exportSpecifiers && hasProperty(exportSpecifiers, name.text)) { + if (!exportEquals && exportSpecifiers && name.text in exportSpecifiers) { for (const specifier of exportSpecifiers[name.text]) { writeLine(); emitStart(specifier.name); @@ -5311,18 +5311,7 @@ const _super = (function (geti, seti) { emitSignatureParameters(ctor); } else { - // Based on EcmaScript6 section 14.5.14: Runtime Semantics: ClassDefinitionEvaluation. - // If constructor is empty, then, - // If ClassHeritageopt is present, then - // Let constructor be the result of parsing the String "constructor(... args){ super (...args);}" using the syntactic grammar with the goal symbol MethodDefinition. - // Else, - // Let constructor be the result of parsing the String "constructor( ){ }" using the syntactic grammar with the goal symbol MethodDefinition - if (baseTypeElement) { - write("(...args)"); - } - else { - write("()"); - } + write("()"); } } @@ -5360,7 +5349,7 @@ const _super = (function (geti, seti) { write("_super.apply(this, arguments);"); } else { - write("super(...args);"); + write("super(...arguments);"); } emitEnd(baseTypeElement); } @@ -6461,7 +6450,7 @@ const _super = (function (geti, seti) { * Here we check if alternative name was provided for a given moduleName and return it if possible. */ function tryRenameExternalModule(moduleName: LiteralExpression): string { - if (renamedDependencies && hasProperty(renamedDependencies, moduleName.text)) { + if (renamedDependencies && moduleName.text in renamedDependencies) { return `"${renamedDependencies[moduleName.text]}"`; } return undefined; @@ -6803,7 +6792,7 @@ const _super = (function (geti, seti) { function collectExternalModuleInfo(sourceFile: SourceFile) { externalImports = []; - exportSpecifiers = {}; + exportSpecifiers = createMap(); exportEquals = undefined; hasExportStarsToExportValues = false; for (const node of sourceFile.statements) { @@ -6941,7 +6930,7 @@ const _super = (function (geti, seti) { } // local names set should only be added if we have anything exported - if (!exportedDeclarations && isEmpty(exportSpecifiers)) { + if (!exportedDeclarations && !someProperties(exportSpecifiers)) { // no exported declarations (export var ...) or export specifiers (export {x}) // check if we have any non star export declarations. let hasExportDeclarationWithExportClause = false; @@ -7081,7 +7070,7 @@ const _super = (function (geti, seti) { if (hoistedVars) { writeLine(); write("var "); - const seen: Map = {}; + const seen = createMap(); for (let i = 0; i < hoistedVars.length; i++) { const local = hoistedVars[i]; const name = local.kind === SyntaxKind.Identifier @@ -7091,7 +7080,7 @@ const _super = (function (geti, seti) { if (name) { // do not emit duplicate entries (in case of declaration merging) in the list of hoisted variables const text = unescapeIdentifier(name.text); - if (hasProperty(seen, text)) { + if (text in seen) { continue; } else { @@ -7447,7 +7436,7 @@ const _super = (function (geti, seti) { writeModuleName(node, emitRelativePathAsModuleName); write("["); - const groupIndices: Map = {}; + const groupIndices = createMap(); const dependencyGroups: DependencyGroup[] = []; for (let i = 0; i < externalImports.length; i++) { @@ -7460,7 +7449,7 @@ const _super = (function (geti, seti) { // for deduplication purposes in key remove leading and trailing quotes so 'a' and "a" will be considered the same const key = text.substr(1, text.length - 2); - if (hasProperty(groupIndices, key)) { + if (key in groupIndices) { // deduplicate/group entries in dependency list by the dependency name const groupIndex = groupIndices[key]; dependencyGroups[groupIndex].push(externalImports[i]); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 014d0926213..aca7d745730 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -421,10 +421,10 @@ namespace ts { } export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { - const start = performance.mark(); + performance.mark("beforeParse"); const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); - - performance.measure("Parse", start); + performance.mark("afterParse"); + performance.measure("Parse", "beforeParse", "afterParse"); return result; } @@ -595,7 +595,7 @@ namespace ts { parseDiagnostics = []; parsingContext = 0; - identifiers = {}; + identifiers = createMap(); identifierCount = 0; nodeCount = 0; @@ -1084,7 +1084,7 @@ namespace ts { function internIdentifier(text: string): string { text = escapeIdentifier(text); - return hasProperty(identifiers, text) ? identifiers[text] : (identifiers[text] = text); + return identifiers[text] || (identifiers[text] = text); } // An identifier that starts with two underscores has an extra underscore character prepended to it to avoid issues diff --git a/src/compiler/performance.ts b/src/compiler/performance.ts index 89db876ae5e..e8f064e81cd 100644 --- a/src/compiler/performance.ts +++ b/src/compiler/performance.ts @@ -6,71 +6,57 @@ namespace ts { } /*@internal*/ +/** Performance measurements for the compiler. */ namespace ts.performance { - /** Performance measurements for the compiler. */ declare const onProfilerEvent: { (markName: string): void; profiler: boolean; }; - let profilerEvent: (markName: string) => void; - let counters: Map; + + const profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true + ? onProfilerEvent + : (markName: string) => { }; + + let enabled = false; + let profilerStart = 0; + let counts: Map; + let marks: Map; let measures: Map; /** - * Emit a performance event if ts-profiler is connected. This is primarily used - * to generate heap snapshots. + * Marks a performance event. * - * @param eventName A name for the event. + * @param markName The name of the mark. */ - export function emit(eventName: string) { - if (profilerEvent) { - profilerEvent(eventName); + export function mark(markName: string) { + if (enabled) { + marks[markName] = timestamp(); + counts[markName] = (counts[markName] || 0) + 1; + profilerEvent(markName); } } - /** - * Increments a counter with the specified name. - * - * @param counterName The name of the counter. - */ - export function increment(counterName: string) { - if (counters) { - counters[counterName] = (getProperty(counters, counterName) || 0) + 1; - } - } - - /** - * Gets the value of the counter with the specified name. - * - * @param counterName The name of the counter. - */ - export function getCount(counterName: string) { - return counters && getProperty(counters, counterName) || 0; - } - - /** - * Marks the start of a performance measurement. - */ - export function mark() { - return measures ? timestamp() : 0; - } - /** * Adds a performance measurement with the specified name. * * @param measureName The name of the performance measurement. - * @param marker The timestamp of the starting mark. + * @param startMarkName The name of the starting mark. If not supplied, the point at which the + * profiler was enabled is used. + * @param endMarkName The name of the ending mark. If not supplied, the current timestamp is + * used. */ - export function measure(measureName: string, marker: number) { - if (measures) { - measures[measureName] = (getProperty(measures, measureName) || 0) + (timestamp() - marker); + export function measure(measureName: string, startMarkName?: string, endMarkName?: string) { + if (enabled) { + const end = endMarkName && marks[endMarkName] || timestamp(); + const start = startMarkName && marks[startMarkName] || profilerStart; + measures[measureName] = (measures[measureName] || 0) + (end - start); } } /** - * Iterate over each measure, performing some action - * - * @param cb The action to perform for each measure + * Gets the number of times a marker was encountered. + * + * @param markName The name of the mark. */ - export function forEachMeasure(cb: (measureName: string, duration: number) => void) { - return forEachKey(measures, key => cb(key, measures[key])); + export function getCount(markName: string) { + return counts && counts[markName] || 0; } /** @@ -79,31 +65,31 @@ namespace ts.performance { * @param measureName The name of the measure whose durations should be accumulated. */ export function getDuration(measureName: string) { - return measures && getProperty(measures, measureName) || 0; + return measures && measures[measureName] || 0; + } + + /** + * Iterate over each measure, performing some action + * + * @param cb The action to perform for each measure + */ + export function forEachMeasure(cb: (measureName: string, duration: number) => void) { + for (const key in measures) { + cb(key, measures[key]); + } } /** Enables (and resets) performance measurements for the compiler. */ export function enable() { - counters = { }; - measures = { - "I/O Read": 0, - "I/O Write": 0, - "Program": 0, - "Parse": 0, - "Bind": 0, - "Check": 0, - "Emit": 0, - }; - - profilerEvent = typeof onProfilerEvent === "function" && onProfilerEvent.profiler === true - ? onProfilerEvent - : undefined; + counts = createMap(); + marks = createMap(); + measures = createMap(); + enabled = true; + profilerStart = timestamp(); } - /** Disables (and clears) performance measurements for the compiler. */ + /** Disables performance measurements for the compiler. */ export function disable() { - counters = undefined; - measures = undefined; - profilerEvent = undefined; + enabled = false; } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4b9948c3ba0..004f4102667 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -119,49 +119,31 @@ namespace ts { } function tryReadTypesSection(packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string { - let jsonContent: { typings?: string, types?: string, main?: string }; - try { - const jsonText = state.host.readFile(packageJsonPath); - jsonContent = jsonText ? <{ typings?: string, types?: string, main?: string }>JSON.parse(jsonText) : {}; - } - catch (e) { - // gracefully handle if readFile fails or returns not JSON - jsonContent = {}; + const jsonContent = readJson(packageJsonPath, state.host); + + function tryReadFromField(fieldName: string) { + if (hasProperty(jsonContent, fieldName)) { + const typesFile = (jsonContent)[fieldName]; + if (typeof typesFile === "string") { + const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile)); + if (state.traceEnabled) { + trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath); + } + return typesFilePath; + } + else { + if (state.traceEnabled) { + trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, fieldName, typeof typesFile); + } + } + } } - let typesFile: string; - let fieldName: string; - // first try to read content of 'typings' section (backward compatibility) - if (jsonContent.typings) { - if (typeof jsonContent.typings === "string") { - fieldName = "typings"; - typesFile = jsonContent.typings; - } - else { - if (state.traceEnabled) { - trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "typings", typeof jsonContent.typings); - } - } - } - // then read 'types' - if (!typesFile && jsonContent.types) { - if (typeof jsonContent.types === "string") { - fieldName = "types"; - typesFile = jsonContent.types; - } - else { - if (state.traceEnabled) { - trace(state.host, Diagnostics.Expected_type_of_0_field_in_package_json_to_be_string_got_1, "types", typeof jsonContent.types); - } - } - } - if (typesFile) { - const typesFilePath = normalizePath(combinePaths(baseDirectory, typesFile)); - if (state.traceEnabled) { - trace(state.host, Diagnostics.package_json_has_0_field_1_that_references_2, fieldName, typesFile, typesFilePath); - } + const typesFilePath = tryReadFromField("typings") || tryReadFromField("types"); + if (typesFilePath) { return typesFilePath; } + // Use the main module for inferring types if no types package specified and the allowJs is set if (state.compilerOptions.allowJs && jsonContent.main && typeof jsonContent.main === "string") { if (state.traceEnabled) { @@ -173,6 +155,17 @@ namespace ts { return undefined; } + function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } { + try { + const jsonText = host.readFile(path); + return jsonText ? JSON.parse(jsonText) : {}; + } + catch (e) { + // gracefully handle if readFile fails or returns not JSON + return {}; + } + } + const typeReferenceExtensions = [".d.ts"]; function getEffectiveTypeRoots(options: CompilerOptions, host: ModuleResolutionHost) { @@ -508,7 +501,7 @@ namespace ts { if (state.traceEnabled) { trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); } - matchedPattern = matchPatternOrExact(getKeys(state.compilerOptions.paths), moduleName); + matchedPattern = matchPatternOrExact(getOwnKeys(state.compilerOptions.paths), moduleName); } if (matchedPattern) { @@ -717,7 +710,7 @@ namespace ts { } function loadNodeModuleFromDirectory(extensions: string[], candidate: string, failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { - const packageJsonPath = combinePaths(candidate, "package.json"); + const packageJsonPath = pathToPackageJson(candidate); const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host); if (directoryExists && state.host.fileExists(packageJsonPath)) { if (state.traceEnabled) { @@ -747,6 +740,10 @@ namespace ts { return loadModuleFromFile(combinePaths(candidate, "index"), extensions, failedLookupLocation, !directoryExists, state); } + function pathToPackageJson(directory: string): string { + return combinePaths(directory, "package.json"); + } + function loadModuleFromNodeModulesFolder(moduleName: string, directory: string, failedLookupLocations: string[], state: ModuleResolutionState): string { const nodeModulesFolder = combinePaths(directory, "node_modules"); const nodeModulesFolderExists = directoryProbablyExists(nodeModulesFolder, state.host); @@ -846,7 +843,7 @@ namespace ts { } export function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost { - const existingDirectories: Map = {}; + const existingDirectories = createMap(); function getCanonicalFileName(fileName: string): string { // if underlying system can distinguish between two files whose names differs only in cases then file name already in canonical form. @@ -860,9 +857,10 @@ namespace ts { function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile { let text: string; try { - const start = performance.mark(); + performance.mark("beforeIORead"); text = sys.readFile(fileName, options.charset); - performance.measure("I/O Read", start); + performance.mark("afterIORead"); + performance.measure("I/O Read", "beforeIORead", "afterIORead"); } catch (e) { if (onError) { @@ -877,7 +875,7 @@ namespace ts { } function directoryExists(directoryPath: string): boolean { - if (hasProperty(existingDirectories, directoryPath)) { + if (directoryPath in existingDirectories) { return true; } if (sys.directoryExists(directoryPath)) { @@ -899,13 +897,13 @@ namespace ts { function writeFileIfUpdated(fileName: string, data: string, writeByteOrderMark: boolean): void { if (!outputFingerprints) { - outputFingerprints = {}; + outputFingerprints = createMap(); } const hash = sys.createHash(data); const mtimeBefore = sys.getModifiedTime(fileName); - if (mtimeBefore && hasProperty(outputFingerprints, fileName)) { + if (mtimeBefore && fileName in outputFingerprints) { const fingerprint = outputFingerprints[fileName]; // If output has not been changed, and the file has no external modification @@ -929,7 +927,7 @@ namespace ts { function writeFile(fileName: string, data: string, writeByteOrderMark: boolean, onError?: (message: string) => void) { try { - const start = performance.mark(); + performance.mark("beforeIOWrite"); ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); if (isWatchSet(options) && sys.createHash && sys.getModifiedTime) { @@ -939,7 +937,8 @@ namespace ts { sys.writeFile(fileName, data, writeByteOrderMark); } - performance.measure("I/O Write", start); + performance.mark("afterIOWrite"); + performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); } catch (e) { if (onError) { @@ -1040,47 +1039,47 @@ namespace ts { return []; } const resolutions: T[] = []; - const cache: Map = {}; + const cache = createMap(); for (const name of names) { - let result: T; - if (hasProperty(cache, name)) { - result = cache[name]; - } - else { - result = loader(name, containingFile); - cache[name] = result; - } + const result = name in cache + ? cache[name] + : cache[name] = loader(name, containingFile); resolutions.push(result); } return resolutions; } - function getInferredTypesRoot(options: CompilerOptions, rootFiles: string[], host: CompilerHost) { - return computeCommonSourceDirectoryOfFilenames(rootFiles, host.getCurrentDirectory(), f => host.getCanonicalFileName(f)); - } - /** - * Given a set of options and a set of root files, returns the set of type directive names + * Given a set of options, returns the set of type directive names * that should be included for this program automatically. * This list could either come from the config file, * or from enumerating the types root + initial secondary types lookup location. * More type directives might appear in the program later as a result of loading actual source files; * this list is only the set of defaults that are implicitly included. */ - export function getAutomaticTypeDirectiveNames(options: CompilerOptions, rootFiles: string[], host: CompilerHost): string[] { + export function getAutomaticTypeDirectiveNames(options: CompilerOptions, host: ModuleResolutionHost): string[] { // Use explicit type list from tsconfig.json if (options.types) { return options.types; } // Walk the primary type lookup locations - let result: string[] = []; + const result: string[] = []; if (host.directoryExists && host.getDirectories) { const typeRoots = getEffectiveTypeRoots(options, host); if (typeRoots) { for (const root of typeRoots) { if (host.directoryExists(root)) { - result = result.concat(host.getDirectories(root)); + for (const typeDirectivePath of host.getDirectories(root)) { + const normalized = normalizePath(typeDirectivePath); + const packageJsonPath = pathToPackageJson(combinePaths(root, normalized)); + // tslint:disable-next-line:no-null-keyword + const isNotNeededPackage = host.fileExists(packageJsonPath) && readJson(packageJsonPath, host).typings === null; + if (!isNotNeededPackage) { + // Return just the type directive names + result.push(getBaseFileName(normalized)); + } + } } } } @@ -1096,7 +1095,7 @@ namespace ts { let noDiagnosticsTypeChecker: TypeChecker; let classifiableNames: Map; - let resolvedTypeReferenceDirectives: Map = {}; + let resolvedTypeReferenceDirectives = createMap(); let fileProcessingDiagnostics = createDiagnosticCollection(); // The below settings are to track if a .js file should be add to the program if loaded via searching under node_modules. @@ -1111,12 +1110,12 @@ namespace ts { // If a module has some of its imports skipped due to being at the depth limit under node_modules, then track // this, as it may be imported at a shallower depth later, and then it will need its skipped imports processed. - const modulesWithElidedImports: Map = {}; + const modulesWithElidedImports = createMap(); // Track source files that are source files found by searching under node_modules, as these shouldn't be compiled. - const sourceFilesFoundSearchingNodeModules: Map = {}; + const sourceFilesFoundSearchingNodeModules = createMap(); - const start = performance.mark(); + performance.mark("beforeProgram"); host = host || createCompilerHost(options); @@ -1155,11 +1154,11 @@ namespace ts { forEach(rootNames, name => processRootFile(name, /*isDefaultLib*/ false)); // load type declarations specified via 'types' argument or implicitly from types/ and node_modules/@types folders - const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, rootNames, host); + const typeReferences: string[] = getAutomaticTypeDirectiveNames(options, host); if (typeReferences) { - const inferredRoot = getInferredTypesRoot(options, rootNames, host); - const containingFilename = combinePaths(inferredRoot, "__inferred type names__.ts"); + // This containingFilename needs to match with the one used in managed-side + const containingFilename = combinePaths(host.getCurrentDirectory(), "__inferred type names__.ts"); const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeReferences, containingFilename); for (let i = 0; i < typeReferences.length; i++) { processTypeReferenceDirective(typeReferences[i], resolutions[i]); @@ -1214,8 +1213,8 @@ namespace ts { }; verifyCompilerOptions(); - - performance.measure("Program", start); + performance.mark("afterProgram"); + performance.measure("Program", "beforeProgram", "afterProgram"); return program; @@ -1242,10 +1241,10 @@ namespace ts { if (!classifiableNames) { // Initialize a checker so that all our files are bound. getTypeChecker(); - classifiableNames = {}; + classifiableNames = createMap(); for (const sourceFile of files) { - copyMap(sourceFile.classifiableNames, classifiableNames); + copyProperties(sourceFile.classifiableNames, classifiableNames); } } @@ -1273,7 +1272,7 @@ namespace ts { (oldOptions.maxNodeModuleJsDepth !== options.maxNodeModuleJsDepth) || !arrayIsEqualTo(oldOptions.typeRoots, oldOptions.typeRoots) || !arrayIsEqualTo(oldOptions.rootDirs, options.rootDirs) || - !mapIsEqualTo(oldOptions.paths, options.paths)) { + !equalOwnProperties(oldOptions.paths, options.paths)) { return false; } @@ -1395,7 +1394,7 @@ namespace ts { getSourceFile: program.getSourceFile, getSourceFileByPath: program.getSourceFileByPath, getSourceFiles: program.getSourceFiles, - isSourceFileFromExternalLibrary: (file: SourceFile) => !!lookUp(sourceFilesFoundSearchingNodeModules, file.path), + isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path], writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), isEmitBlocked, @@ -1458,14 +1457,15 @@ namespace ts { // checked is to not pass the file to getEmitResolver. const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile); - const start = performance.mark(); + performance.mark("beforeEmit"); const emitResult = emitFiles( emitResolver, getEmitHost(writeFileCallback), sourceFile); - performance.measure("Emit", start); + performance.mark("afterEmit"); + performance.measure("Emit", "beforeEmit", "afterEmit"); return emitResult; } @@ -1932,7 +1932,7 @@ namespace ts { // If the file was previously found via a node_modules search, but is now being processed as a root file, // then everything it sucks in may also be marked incorrectly, and needs to be checked again. - if (file && lookUp(sourceFilesFoundSearchingNodeModules, file.path) && currentNodeModulesDepth == 0) { + if (file && sourceFilesFoundSearchingNodeModules[file.path] && currentNodeModulesDepth == 0) { sourceFilesFoundSearchingNodeModules[file.path] = false; if (!options.noResolve) { processReferencedFiles(file, getDirectoryPath(fileName), isDefaultLib); @@ -1943,7 +1943,7 @@ namespace ts { processImportedModules(file, getDirectoryPath(fileName)); } // See if we need to reprocess the imports due to prior skipped imports - else if (file && lookUp(modulesWithElidedImports, file.path)) { + else if (file && modulesWithElidedImports[file.path]) { if (currentNodeModulesDepth < maxNodeModulesJsDepth) { modulesWithElidedImports[file.path] = false; processImportedModules(file, getDirectoryPath(fileName)); @@ -2010,15 +2010,16 @@ namespace ts { } function processTypeReferenceDirectives(file: SourceFile) { - const typeDirectives = map(file.typeReferenceDirectives, l => l.fileName); + const typeDirectives = map(file.typeReferenceDirectives, ref => ref.fileName.toLocaleLowerCase()); const resolutions = resolveTypeReferenceDirectiveNamesWorker(typeDirectives, file.fileName); for (let i = 0; i < typeDirectives.length; i++) { const ref = file.typeReferenceDirectives[i]; const resolvedTypeReferenceDirective = resolutions[i]; // store resolved type directive on the file - setResolvedTypeReferenceDirective(file, ref.fileName, resolvedTypeReferenceDirective); - processTypeReferenceDirective(ref.fileName, resolvedTypeReferenceDirective, file, ref.pos, ref.end); + const fileName = ref.fileName.toLocaleLowerCase(); + setResolvedTypeReferenceDirective(file, fileName, resolvedTypeReferenceDirective); + processTypeReferenceDirective(fileName, resolvedTypeReferenceDirective, file, ref.pos, ref.end); } } @@ -2043,7 +2044,7 @@ namespace ts { const otherFileText = host.readFile(resolvedTypeReferenceDirective.resolvedFileName); if (otherFileText !== getSourceFile(previousResolution.resolvedFileName).text) { fileProcessingDiagnostics.add(createDiagnostic(refFile, refPos, refEnd, - Diagnostics.Conflicting_library_definitions_for_0_found_at_1_and_2_Copy_the_correct_file_to_the_typings_folder_to_resolve_this_conflict, + Diagnostics.Conflicting_definitions_for_0_found_at_1_and_2_Consider_installing_a_specific_version_of_this_library_to_resolve_the_conflict, typeReferenceDirective, resolvedTypeReferenceDirective.resolvedFileName, previousResolution.resolvedFileName @@ -2083,7 +2084,7 @@ namespace ts { function processImportedModules(file: SourceFile, basePath: string) { collectExternalModuleReferences(file); if (file.imports.length || file.moduleAugmentations.length) { - file.resolvedModules = {}; + file.resolvedModules = createMap(); const moduleNames = map(concatenate(file.imports, file.moduleAugmentations), getTextOfLiteral); const resolutions = resolveModuleNamesWorker(moduleNames, getNormalizedAbsolutePath(file.fileName, currentDirectory)); for (let i = 0; i < moduleNames.length; i++) { diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 6dd84298008..c1431ca23ed 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -55,7 +55,7 @@ namespace ts { tryScan(callback: () => T): T; } - const textToToken: Map = { + const textToToken = createMap({ "abstract": SyntaxKind.AbstractKeyword, "any": SyntaxKind.AnyKeyword, "as": SyntaxKind.AsKeyword, @@ -179,7 +179,7 @@ namespace ts { "|=": SyntaxKind.BarEqualsToken, "^=": SyntaxKind.CaretEqualsToken, "@": SyntaxKind.AtToken, - }; + }); /* As per ECMAScript Language Specification 3th Edition, Section 7.6: Identifiers @@ -274,9 +274,7 @@ namespace ts { function makeReverseMap(source: Map): string[] { const result: string[] = []; for (const name in source) { - if (source.hasOwnProperty(name)) { - result[source[name]] = name; - } + result[source[name]] = name; } return result; } diff --git a/src/compiler/sourcemap.ts b/src/compiler/sourcemap.ts index 2d8c36a3d02..4046867faf1 100644 --- a/src/compiler/sourcemap.ts +++ b/src/compiler/sourcemap.ts @@ -46,6 +46,7 @@ namespace ts { export function createSourceMapWriter(host: EmitHost, writer: EmitTextWriter): SourceMapWriter { const compilerOptions = host.getCompilerOptions(); + const extendedDiagnostics = compilerOptions.extendedDiagnostics; let currentSourceFile: SourceFile; let sourceMapDir: string; // The directory in which sourcemap will be let stopOverridingSpan = false; @@ -240,7 +241,9 @@ namespace ts { return; } - const start = performance.mark(); + if (extendedDiagnostics) { + performance.mark("beforeSourcemap"); + } const sourceLinePos = getLineAndCharacterOfPosition(currentSourceFile, pos); @@ -282,7 +285,10 @@ namespace ts { updateLastEncodedAndRecordedSpans(); - performance.measure("Source Map", start); + if (extendedDiagnostics) { + performance.mark("afterSourcemap"); + performance.measure("Source Map", "beforeSourcemap", "afterSourcemap"); + } } function getStartPos(range: TextRange) { diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 29ae2c60af1..350d75429b7 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -233,15 +233,15 @@ namespace ts { const useNonPollingWatchers = process.env["TSC_NONPOLLING_WATCHER"]; function createWatchedFileSet() { - const dirWatchers: Map = {}; + const dirWatchers = createMap(); // One file can have multiple watchers - const fileWatcherCallbacks: Map = {}; + const fileWatcherCallbacks = createMap(); return { addFile, removeFile }; function reduceDirWatcherRefCountForFile(fileName: string) { const dirName = getDirectoryPath(fileName); - if (hasProperty(dirWatchers, dirName)) { - const watcher = dirWatchers[dirName]; + const watcher = dirWatchers[dirName]; + if (watcher) { watcher.referenceCount -= 1; if (watcher.referenceCount <= 0) { watcher.close(); @@ -251,13 +251,12 @@ namespace ts { } function addDirWatcher(dirPath: string): void { - if (hasProperty(dirWatchers, dirPath)) { - const watcher = dirWatchers[dirPath]; + let watcher = dirWatchers[dirPath]; + if (watcher) { watcher.referenceCount += 1; return; } - - const watcher: DirectoryWatcher = _fs.watch( + watcher = _fs.watch( dirPath, { persistent: true }, (eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath) @@ -268,12 +267,7 @@ namespace ts { } function addFileWatcherCallback(filePath: string, callback: FileWatcherCallback): void { - if (hasProperty(fileWatcherCallbacks, filePath)) { - fileWatcherCallbacks[filePath].push(callback); - } - else { - fileWatcherCallbacks[filePath] = [callback]; - } + (fileWatcherCallbacks[filePath] || (fileWatcherCallbacks[filePath] = [])).push(callback); } function addFile(fileName: string, callback: FileWatcherCallback): WatchedFile { @@ -289,8 +283,9 @@ namespace ts { } function removeFileWatcherCallback(filePath: string, callback: FileWatcherCallback) { - if (hasProperty(fileWatcherCallbacks, filePath)) { - const newCallbacks = copyListRemovingItem(callback, fileWatcherCallbacks[filePath]); + const callbacks = fileWatcherCallbacks[filePath]; + if (callbacks) { + const newCallbacks = copyListRemovingItem(callback, callbacks); if (newCallbacks.length === 0) { delete fileWatcherCallbacks[filePath]; } @@ -306,7 +301,7 @@ namespace ts { ? undefined : ts.getNormalizedAbsolutePath(relativeFileName, baseDirPath); // Some applications save a working file via rename operations - if ((eventName === "change" || eventName === "rename") && hasProperty(fileWatcherCallbacks, fileName)) { + if ((eventName === "change" || eventName === "rename") && fileWatcherCallbacks[fileName]) { for (const fileCallback of fileWatcherCallbacks[fileName]) { fileCallback(fileName); } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 10538d0c009..8b445bcb626 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -122,11 +122,11 @@ namespace ts { const gutterSeparator = " "; const resetEscapeSequence = "\u001b[0m"; const ellipsis = "..."; - const categoryFormatMap: Map = { + const categoryFormatMap = createMap({ [DiagnosticCategory.Warning]: yellowForegroundEscapeSequence, [DiagnosticCategory.Error]: redForegroundEscapeSequence, [DiagnosticCategory.Message]: blueForegroundEscapeSequence, - }; + }); function formatAndReset(text: string, formatStyle: string) { return formatStyle + text + resetEscapeSequence; @@ -432,7 +432,7 @@ namespace ts { } // reset the cache of existing files - cachedExistingFiles = {}; + cachedExistingFiles = createMap(); const compileResult = compile(rootFileNames, compilerOptions, compilerHost); @@ -445,10 +445,9 @@ namespace ts { } function cachedFileExists(fileName: string): boolean { - if (hasProperty(cachedExistingFiles, fileName)) { - return cachedExistingFiles[fileName]; - } - return cachedExistingFiles[fileName] = hostFileExists(fileName); + return fileName in cachedExistingFiles + ? cachedExistingFiles[fileName] + : cachedExistingFiles[fileName] = hostFileExists(fileName); } function getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void) { @@ -676,7 +675,7 @@ namespace ts { const usageColumn: string[] = []; // Things like "-d, --declaration" go in here. const descriptionColumn: string[] = []; - const optionsDescriptionMap: Map = {}; // Map between option.description and list of option.type if it is a kind + const optionsDescriptionMap = createMap(); // Map between option.description and list of option.type if it is a kind for (let i = 0; i < optsList.length; i++) { const option = optsList[i]; @@ -704,9 +703,10 @@ namespace ts { description = getDiagnosticText(option.description); const options: string[] = []; const element = (option).element; - forEachKey(>element.type, key => { + const typeMap = >element.type; + for (const key in typeMap) { options.push(`'${key}'`); - }); + } optionsDescriptionMap[description] = options; } else { @@ -786,7 +786,7 @@ namespace ts { return; function serializeCompilerOptions(options: CompilerOptions): Map { - const result: Map = {}; + const result = createMap(); const optionsNameMap = getOptionNameMap().optionNameMap; for (const name in options) { @@ -812,9 +812,8 @@ namespace ts { // Enum const typeMap = >optionDefinition.type; for (const key in typeMap) { - if (hasProperty(typeMap, key)) { - if (typeMap[key] === value) - result[name] = key; + if (typeMap[key] === value) { + result[name] = key; } } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 70bd3c3c070..b5e63ea6e07 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,9 +1,13 @@ - namespace ts { - export interface Map { + + export interface MapLike { [index: string]: T; } + export interface Map extends MapLike { + __mapBrand: any; + } + // branded string type used to store absolute, normalized and canonicalized paths // arbitrary file name can be converted to Path via toPath function export type Path = string & { __pathBrand: any }; @@ -982,13 +986,19 @@ namespace ts { multiLine?: boolean; } + export type EntityNameExpression = Identifier | PropertyAccessEntityNameExpression; + export type EntityNameOrEntityNameExpression = EntityName | EntityNameExpression; + // @kind(SyntaxKind.PropertyAccessExpression) export interface PropertyAccessExpression extends MemberExpression, Declaration { expression: LeftHandSideExpression; name: Identifier; } - - export type IdentifierOrPropertyAccess = Identifier | PropertyAccessExpression; + /** Brand for a PropertyAccessExpression which, like a QualifiedName, consists of a sequence of identifiers separated by dots. */ + export interface PropertyAccessEntityNameExpression extends PropertyAccessExpression { + _propertyAccessExpressionLikeQualifiedNameBrand?: any; + expression: EntityNameExpression; + } // @kind(SyntaxKind.ElementAccessExpression) export interface ElementAccessExpression extends MemberExpression { @@ -1606,6 +1616,16 @@ namespace ts { antecedent: FlowNode; } + export type FlowType = Type | IncompleteType; + + // Incomplete types occur during control flow analysis of loops. An IncompleteType + // is distinguished from a regular type by a flags value of zero. Incomplete type + // objects are internal to the getFlowTypeOfRefecence function and never escape it. + export interface IncompleteType { + flags: TypeFlags; // No flags set + type: Type; // The type marked incomplete + } + export interface AmdDependency { path: string; name: string; @@ -2021,7 +2041,7 @@ namespace ts { writeTypeOfExpression(expr: Expression, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; writeBaseConstructorTypeOfClass(node: ClassLikeDeclaration, enclosingDeclaration: Node, flags: TypeFormatFlags, writer: SymbolWriter): void; isSymbolAccessible(symbol: Symbol, enclosingDeclaration: Node, meaning: SymbolFlags): SymbolAccessibilityResult; - isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult; + isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult; // Returns the constant value this property access resolves to, or 'undefined' for a non-constant getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; getReferencedValueDeclaration(reference: Identifier): Declaration; @@ -2030,7 +2050,7 @@ namespace ts { moduleExportsSomeValue(moduleReferenceExpression: Expression): boolean; isArgumentsLocalBinding(node: Identifier): boolean; getExternalModuleFileFromDeclaration(declaration: ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ModuleDeclaration): SourceFile; - getTypeReferenceDirectivesForEntityName(name: EntityName | PropertyAccessExpression): string[]; + getTypeReferenceDirectivesForEntityName(name: EntityNameOrEntityNameExpression): string[]; getTypeReferenceDirectivesForSymbol(symbol: Symbol, meaning?: SymbolFlags): string[]; } @@ -2138,6 +2158,7 @@ namespace ts { /* @internal */ constEnumOnlyModule?: boolean; // True if module contains only const enums or other modules with only const enums /* @internal */ isReferenced?: boolean; // True if the symbol is referenced elsewhere /* @internal */ isReplaceableByMethod?: boolean; // Can this Javascript class property be replaced by a method symbol? + /* @internal */ isAssigned?: boolean; // True if the symbol is a parameter with assignments } /* @internal */ @@ -2151,6 +2172,8 @@ namespace ts { mapper?: TypeMapper; // Type mapper for instantiation alias referenced?: boolean; // True if alias symbol has been referenced as a value containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property + hasCommonType?: boolean; // True if constituents of synthetic property all have same type + isDiscriminantProperty?: boolean; // True if discriminant synthetic property resolvedExports?: SymbolTable; // Resolved exports of module exportsChecked?: boolean; // True if exports of external module have been checked isDeclarationWithCollidingName?: boolean; // True if symbol is block scoped redeclaration @@ -2161,9 +2184,7 @@ namespace ts { /* @internal */ export interface TransientSymbol extends Symbol, SymbolLinks { } - export interface SymbolTable { - [index: string]: Symbol; - } + export type SymbolTable = Map; /** Represents a "prefix*suffix" pattern. */ /* @internal */ @@ -2190,23 +2211,24 @@ namespace ts { AsyncMethodWithSuper = 0x00000800, // An async method that reads a value from a member of 'super'. AsyncMethodWithSuperBinding = 0x00001000, // An async method that assigns a value to a member of 'super'. CaptureArguments = 0x00002000, // Lexical 'arguments' used in body (for async functions) - EnumValuesComputed = 0x00004000, // Values for enum members have been computed, and any errors have been reported for them. - LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration. - LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure - CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function - BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement - ClassWithBodyScopedClassBinding = 0x00080000, // Decorated class that contains a binding to itself inside of the class body. - BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body. - NeedsLoopOutParameter = 0x00200000, // Block scoped binding whose value should be explicitly copied outside of the converted loop + EnumValuesComputed = 0x00004000, // Values for enum members have been computed, and any errors have been reported for them. + LexicalModuleMergesWithClass = 0x00008000, // Instantiated lexical module declaration is merged with a previous class declaration. + LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure + CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function + BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement + ClassWithBodyScopedClassBinding = 0x00080000, // Decorated class that contains a binding to itself inside of the class body. + BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body. + NeedsLoopOutParameter = 0x00200000, // Block scoped binding whose value should be explicitly copied outside of the converted loop + AssignmentsMarked = 0x00400000, // Parameter assignments have been marked } /* @internal */ export interface NodeLinks { + flags?: NodeCheckFlags; // Set of flags specific to Node resolvedType?: Type; // Cached type of type node resolvedSignature?: Signature; // Cached signature of signature node or call expression resolvedSymbol?: Symbol; // Cached name resolution result resolvedIndexInfo?: IndexInfo; // Cached indexing info resolution result - flags?: NodeCheckFlags; // Set of flags specific to Node enumMemberValue?: number; // Constant value of enum member isVisible?: boolean; // Is this node visible hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context @@ -2360,6 +2382,7 @@ namespace ts { export interface TupleType extends ObjectType { elementTypes: Type[]; // Element types + thisType?: Type; // This-type of tuple (only needed for tuples that are constraints of type parameters) } export interface UnionOrIntersectionType extends Type { @@ -2546,7 +2569,7 @@ namespace ts { } export type RootPaths = string[]; - export type PathSubstitutions = Map; + export type PathSubstitutions = MapLike; export type TsConfigOnlyOptions = RootPaths | PathSubstitutions; export type CompilerOptionsValue = string | number | boolean | (string | number)[] | TsConfigOnlyOptions; @@ -2706,7 +2729,7 @@ namespace ts { fileNames: string[]; raw?: any; errors: Diagnostic[]; - wildcardDirectories?: Map; + wildcardDirectories?: MapLike; } export const enum WatchDirectoryFlags { @@ -2716,7 +2739,7 @@ namespace ts { export interface ExpandResult { fileNames: string[]; - wildcardDirectories: Map; + wildcardDirectories: MapLike; } /* @internal */ @@ -2901,6 +2924,7 @@ namespace ts { directoryExists?(directoryName: string): boolean; realpath?(path: string): string; getCurrentDirectory?(): string; + getDirectories?(path: string): string[]; } export interface ResolvedModule { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4ae0005e9c5..0a1f43203ce 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -87,25 +87,6 @@ namespace ts { return node.end - node.pos; } - export function mapIsEqualTo(map1: Map, map2: Map): boolean { - if (!map1 || !map2) { - return map1 === map2; - } - return containsAll(map1, map2) && containsAll(map2, map1); - } - - function containsAll(map: Map, other: Map): boolean { - for (const key in map) { - if (!hasProperty(map, key)) { - continue; - } - if (!hasProperty(other, key) || map[key] !== other[key]) { - return false; - } - } - return true; - } - export function arrayIsEqualTo(array1: T[], array2: T[], equaler?: (a: T, b: T) => boolean): boolean { if (!array1 || !array2) { return array1 === array2; @@ -126,7 +107,7 @@ namespace ts { } export function hasResolvedModule(sourceFile: SourceFile, moduleNameText: string): boolean { - return sourceFile.resolvedModules && hasProperty(sourceFile.resolvedModules, moduleNameText); + return !!(sourceFile.resolvedModules && sourceFile.resolvedModules[moduleNameText]); } export function getResolvedModule(sourceFile: SourceFile, moduleNameText: string): ResolvedModule { @@ -135,7 +116,7 @@ namespace ts { export function setResolvedModule(sourceFile: SourceFile, moduleNameText: string, resolvedModule: ResolvedModule): void { if (!sourceFile.resolvedModules) { - sourceFile.resolvedModules = {}; + sourceFile.resolvedModules = createMap(); } sourceFile.resolvedModules[moduleNameText] = resolvedModule; @@ -143,7 +124,7 @@ namespace ts { export function setResolvedTypeReferenceDirective(sourceFile: SourceFile, typeReferenceDirectiveName: string, resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective): void { if (!sourceFile.resolvedTypeReferenceDirectiveNames) { - sourceFile.resolvedTypeReferenceDirectiveNames = {}; + sourceFile.resolvedTypeReferenceDirectiveNames = createMap(); } sourceFile.resolvedTypeReferenceDirectiveNames[typeReferenceDirectiveName] = resolvedTypeReferenceDirective; @@ -166,7 +147,7 @@ namespace ts { } for (let i = 0; i < names.length; i++) { const newResolution = newResolutions[i]; - const oldResolution = oldResolutions && hasProperty(oldResolutions, names[i]) ? oldResolutions[names[i]] : undefined; + const oldResolution = oldResolutions && oldResolutions[names[i]]; const changed = oldResolution ? !newResolution || !comparer(oldResolution, newResolution) @@ -1033,14 +1014,14 @@ namespace ts { && (node).expression.kind === SyntaxKind.SuperKeyword; } - - export function getEntityNameFromTypeNode(node: TypeNode): EntityName | Expression { + export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression { if (node) { switch (node.kind) { case SyntaxKind.TypeReference: return (node).typeName; case SyntaxKind.ExpressionWithTypeArguments: - return (node).expression; + Debug.assert(isEntityNameExpression((node).expression)); + return (node).expression; case SyntaxKind.Identifier: case SyntaxKind.QualifiedName: return (node); @@ -1569,6 +1550,7 @@ namespace ts { case SyntaxKind.MethodSignature: case SyntaxKind.ModuleDeclaration: case SyntaxKind.NamespaceImport: + case SyntaxKind.NamespaceExportDeclaration: case SyntaxKind.Parameter: case SyntaxKind.PropertyAssignment: case SyntaxKind.PropertyDeclaration: @@ -1693,8 +1675,8 @@ namespace ts { // import * as from ... // import { x as } from ... // export { x as } from ... - // export = ... - // export default ... + // export = + // export default export function isAliasSymbolDeclaration(node: Node): boolean { return node.kind === SyntaxKind.ImportEqualsDeclaration || node.kind === SyntaxKind.NamespaceExportDeclaration || @@ -1702,7 +1684,11 @@ namespace ts { node.kind === SyntaxKind.NamespaceImport || node.kind === SyntaxKind.ImportSpecifier || node.kind === SyntaxKind.ExportSpecifier || - node.kind === SyntaxKind.ExportAssignment && (node).expression.kind === SyntaxKind.Identifier; + node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node); + } + + export function exportAssignmentIsAlias(node: ExportAssignment): boolean { + return isEntityNameExpression(node.expression); } export function getClassExtendsHeritageClauseElement(node: ClassLikeDeclaration | InterfaceDeclaration) { @@ -1965,7 +1951,7 @@ namespace ts { export function createDiagnosticCollection(): DiagnosticCollection { let nonFileDiagnostics: Diagnostic[] = []; - const fileDiagnostics: Map = {}; + const fileDiagnostics = createMap(); let diagnosticsModified = false; let modificationCount = 0; @@ -1983,12 +1969,11 @@ namespace ts { } function reattachFileDiagnostics(newFile: SourceFile): void { - if (!hasProperty(fileDiagnostics, newFile.fileName)) { - return; - } - - for (const diagnostic of fileDiagnostics[newFile.fileName]) { - diagnostic.file = newFile; + const diagnostics = fileDiagnostics[newFile.fileName]; + if (diagnostics) { + for (const diagnostic of diagnostics) { + diagnostic.file = newFile; + } } } @@ -2029,9 +2014,7 @@ namespace ts { forEach(nonFileDiagnostics, pushDiagnostic); for (const key in fileDiagnostics) { - if (hasProperty(fileDiagnostics, key)) { - forEach(fileDiagnostics[key], pushDiagnostic); - } + forEach(fileDiagnostics[key], pushDiagnostic); } return sortAndDeduplicateDiagnostics(allDiagnostics); @@ -2046,9 +2029,7 @@ namespace ts { nonFileDiagnostics = sortAndDeduplicateDiagnostics(nonFileDiagnostics); for (const key in fileDiagnostics) { - if (hasProperty(fileDiagnostics, key)) { - fileDiagnostics[key] = sortAndDeduplicateDiagnostics(fileDiagnostics[key]); - } + fileDiagnostics[key] = sortAndDeduplicateDiagnostics(fileDiagnostics[key]); } } } @@ -2059,7 +2040,7 @@ namespace ts { // the map below must be updated. Note that this regexp *does not* include the 'delete' character. // There is no reason for this other than that JSON.stringify does not handle it either. const escapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g; - const escapedCharsMap: Map = { + const escapedCharsMap = createMap({ "\0": "\\0", "\t": "\\t", "\v": "\\v", @@ -2072,7 +2053,7 @@ namespace ts { "\u2028": "\\u2028", // lineSeparator "\u2029": "\\u2029", // paragraphSeparator "\u0085": "\\u0085" // nextLine - }; + }); /** @@ -2680,22 +2661,9 @@ namespace ts { isClassLike(node.parent.parent); } - // Returns false if this heritage clause element's expression contains something unsupported - // (i.e. not a name or dotted name). - export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean { - return isSupportedExpressionWithTypeArgumentsRest(node.expression); - } - - function isSupportedExpressionWithTypeArgumentsRest(node: Expression): boolean { - if (node.kind === SyntaxKind.Identifier) { - return true; - } - else if (isPropertyAccessExpression(node)) { - return isSupportedExpressionWithTypeArgumentsRest(node.expression); - } - else { - return false; - } + export function isEntityNameExpression(node: Expression): node is EntityNameExpression { + return node.kind === SyntaxKind.Identifier || + node.kind === SyntaxKind.PropertyAccessExpression && isEntityNameExpression((node).expression); } export function isRightSideOfQualifiedNameOrPropertyAccess(node: Node) { @@ -2805,7 +2773,7 @@ namespace ts { } function stringifyObject(value: any) { - return `{${reduceProperties(value, stringifyProperty, "")}}`; + return `{${reduceOwnProperties(value, stringifyProperty, "")}}`; } function stringifyProperty(memo: string, value: any, key: string) { diff --git a/src/harness/compilerRunner.ts b/src/harness/compilerRunner.ts index ecdc18394b0..66396293dc2 100644 --- a/src/harness/compilerRunner.ts +++ b/src/harness/compilerRunner.ts @@ -52,7 +52,7 @@ class CompilerBaselineRunner extends RunnerBase { private makeUnitName(name: string, root: string) { const path = ts.toPath(name, root, (fileName) => Harness.Compiler.getCanonicalFileName(fileName)); const pathStart = ts.toPath(Harness.IO.getCurrentDirectory(), "", (fileName) => Harness.Compiler.getCanonicalFileName(fileName)); - return path.replace(pathStart, "/"); + return pathStart ? path.replace(pathStart, "/") : path; }; public checkTestCodeOutput(fileName: string) { @@ -291,8 +291,8 @@ class CompilerBaselineRunner extends RunnerBase { const fullWalker = new TypeWriterWalker(program, /*fullTypeCheck*/ true); - const fullResults: ts.Map = {}; - const pullResults: ts.Map = {}; + const fullResults = ts.createMap(); + const pullResults = ts.createMap(); for (const sourceFile of allFiles) { fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName); diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a42abbbc609..b5fa53763cb 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -95,14 +95,14 @@ namespace FourSlash { export import IndentStyle = ts.IndentStyle; - const entityMap: ts.Map = { + const entityMap = ts.createMap({ "&": "&", "\"": """, "'": "'", "/": "/", "<": "<", ">": ">" - }; + }); export function escapeXmlAttributeValue(s: string) { return s.replace(/[&<>"'\/]/g, ch => entityMap[ch]); @@ -204,7 +204,7 @@ namespace FourSlash { public formatCodeOptions: ts.FormatCodeOptions; - private inputFiles: ts.Map = {}; // Map between inputFile's fileName and its content for easily looking up when resolving references + private inputFiles = ts.createMap(); // Map between inputFile's fileName and its content for easily looking up when resolving references // Add input file which has matched file name with the given reference-file path. // This is necessary when resolveReference flag is specified @@ -249,6 +249,7 @@ namespace FourSlash { if (compilationOptions.typeRoots) { compilationOptions.typeRoots = compilationOptions.typeRoots.map(p => ts.getNormalizedAbsolutePath(p, this.basePath)); } + compilationOptions.skipDefaultLibCheck = true; const languageServiceAdapter = this.getLanguageServiceAdapter(testType, this.cancellationToken, compilationOptions); this.languageServiceAdapterHost = languageServiceAdapter.getHost(); @@ -300,11 +301,11 @@ namespace FourSlash { } else { // resolveReference file-option is not specified then do not resolve any files and include all inputFiles - ts.forEachKey(this.inputFiles, fileName => { + for (const fileName in this.inputFiles) { if (!Harness.isDefaultLibraryFile(fileName)) { this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName], /*isRootFile*/ true); } - }); + } this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName, Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false); } @@ -376,7 +377,7 @@ namespace FourSlash { public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, negative: boolean) { const startMarker = this.getMarkerByName(startMarkerName); const endMarker = this.getMarkerByName(endMarkerName); - const predicate = function (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { + const predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { return ((errorMinChar === startPos) && (errorLimChar === endPos)) ? true : false; }; @@ -428,12 +429,12 @@ namespace FourSlash { let predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean; if (after) { - predicate = function (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { + predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { return ((errorMinChar >= startPos) && (errorLimChar >= startPos)) ? true : false; }; } else { - predicate = function (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { + predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) { return ((errorMinChar <= startPos) && (errorLimChar <= startPos)) ? true : false; }; } @@ -458,7 +459,7 @@ namespace FourSlash { endPos = endMarker.position; } - errors.forEach(function (error: ts.Diagnostic) { + errors.forEach(function(error: ts.Diagnostic) { if (predicate(error.start, error.start + error.length, startPos, endPos)) { exists = true; } @@ -475,7 +476,7 @@ namespace FourSlash { Harness.IO.log("Unexpected error(s) found. Error list is:"); } - errors.forEach(function (error: ts.Diagnostic) { + errors.forEach(function(error: ts.Diagnostic) { Harness.IO.log(" minChar: " + error.start + ", limChar: " + (error.start + error.length) + ", message: " + ts.flattenDiagnosticMessageText(error.messageText, Harness.IO.newLine()) + "\n"); @@ -593,9 +594,9 @@ namespace FourSlash { public noItemsWithSameNameButDifferentKind(): void { const completions = this.getCompletionListAtCaret(); - const uniqueItems: ts.Map = {}; + const uniqueItems = ts.createMap(); for (const item of completions.entries) { - if (!ts.hasProperty(uniqueItems, item.name)) { + if (!(item.name in uniqueItems)) { uniqueItems[item.name] = item.kind; } else { @@ -773,7 +774,7 @@ namespace FourSlash { } public verifyRangesWithSameTextReferenceEachOther() { - ts.forEachValue(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); + ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges)); } private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) { @@ -1348,14 +1349,7 @@ namespace FourSlash { } // Enters lines of text at the current caret position - public type(text: string) { - return this.typeHighFidelity(text); - } - - // Enters lines of text at the current caret position, invoking - // language service APIs to mimic Visual Studio's behavior - // as much as possible - private typeHighFidelity(text: string) { + public type(text: string, highFidelity = false) { let offset = this.currentCaretPosition; const prevChar = " "; const checkCadence = (text.length >> 2) + 1; @@ -1364,24 +1358,26 @@ namespace FourSlash { // Make the edit const ch = text.charAt(i); this.languageServiceAdapterHost.editScript(this.activeFile.fileName, offset, offset, ch); - this.languageService.getBraceMatchingAtPosition(this.activeFile.fileName, offset); + if (highFidelity) { + this.languageService.getBraceMatchingAtPosition(this.activeFile.fileName, offset); + } this.updateMarkersForEdit(this.activeFile.fileName, offset, offset, ch); offset++; - if (ch === "(" || ch === ",") { - /* Signature help*/ - this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset); - } - else if (prevChar === " " && /A-Za-z_/.test(ch)) { - /* Completions */ - this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset); - } + if (highFidelity) { + if (ch === "(" || ch === ",") { + /* Signature help*/ + this.languageService.getSignatureHelpItems(this.activeFile.fileName, offset); + } + else if (prevChar === " " && /A-Za-z_/.test(ch)) { + /* Completions */ + this.languageService.getCompletionsAtPosition(this.activeFile.fileName, offset); + } - if (i % checkCadence === 0) { - this.checkPostEditInvariants(); - // this.languageService.getSyntacticDiagnostics(this.activeFile.fileName); - // this.languageService.getSemanticDiagnostics(this.activeFile.fileName); + if (i % checkCadence === 0) { + this.checkPostEditInvariants(); + } } // Handle post-keystroke formatting @@ -1389,14 +1385,12 @@ namespace FourSlash { const edits = this.languageService.getFormattingEditsAfterKeystroke(this.activeFile.fileName, offset, ch, this.formatCodeOptions); if (edits.length) { offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); - // this.checkPostEditInvariants(); } } } // Move the caret to wherever we ended up this.currentCaretPosition = offset; - this.fixCaretPosition(); this.checkPostEditInvariants(); } @@ -1415,7 +1409,6 @@ namespace FourSlash { const edits = this.languageService.getFormattingEditsForRange(this.activeFile.fileName, start, offset, this.formatCodeOptions); if (edits.length) { offset += this.applyEdits(this.activeFile.fileName, edits, /*isFormattingEdit*/ true); - this.checkPostEditInvariants(); } } @@ -1639,10 +1632,11 @@ namespace FourSlash { } public rangesByText(): ts.Map { - const result: ts.Map = {}; + const result = ts.createMap(); for (const range of this.getRanges()) { const text = this.rangeText(range); - (ts.getProperty(result, text) || (result[text] = [])).push(range); + const ranges = result[text] || (result[text] = []); + ranges.push(range); } return result; } @@ -1897,7 +1891,7 @@ namespace FourSlash { public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) { - const openBraceMap: ts.Map = { + const openBraceMap = ts.createMap({ "(": ts.CharacterCodes.openParen, "{": ts.CharacterCodes.openBrace, "[": ts.CharacterCodes.openBracket, @@ -1905,7 +1899,7 @@ namespace FourSlash { '"': ts.CharacterCodes.doubleQuote, "`": ts.CharacterCodes.backtick, "<": ts.CharacterCodes.lessThan - }; + }); const charCode = openBraceMap[openingBrace]; @@ -2269,40 +2263,12 @@ namespace FourSlash { export function runFourSlashTestContent(basePath: string, testType: FourSlashTestType, content: string, fileName: string): void { // Parse out the files and their metadata const testData = parseTestData(basePath, content, fileName); - const state = new TestState(basePath, testType, testData); - - let result = ""; - const fourslashFile: Harness.Compiler.TestFile = { - unitName: Harness.Compiler.fourslashFileName, - content: undefined, - }; - const testFile: Harness.Compiler.TestFile = { - unitName: fileName, - content: content - }; - - const host = Harness.Compiler.createCompilerHost( - [fourslashFile, testFile], - (fn, contents) => result = contents, - ts.ScriptTarget.Latest, - Harness.IO.useCaseSensitiveFileNames(), - Harness.IO.getCurrentDirectory()); - - const program = ts.createProgram([Harness.Compiler.fourslashFileName, fileName], { outFile: "fourslashTestOutput.js", noResolve: true, target: ts.ScriptTarget.ES3 }, host); - - const sourceFile = host.getSourceFile(fileName, ts.ScriptTarget.ES3); - - const diagnostics = ts.getPreEmitDiagnostics(program, sourceFile); - if (diagnostics.length > 0) { - throw new Error(`Error compiling ${fileName}: ` + - diagnostics.map(e => ts.flattenDiagnosticMessageText(e.messageText, Harness.IO.newLine())).join("\r\n")); + const output = ts.transpileModule(content, { reportDiagnostics: true }); + if (output.diagnostics.length > 0) { + throw new Error(`Syntax error in ${basePath}: ${output.diagnostics[0].messageText}`); } - - program.emit(sourceFile); - - ts.Debug.assert(!!result); - runCode(result, state); + runCode(output.outputText, state); } function runCode(code: string, state: TestState): void { @@ -2395,13 +2361,14 @@ ${code} // Comment line, check for global/file @options and record them const match = optionRegex.exec(line.substr(2)); if (match) { - const fileMetadataNamesIndex = fileMetadataNames.indexOf(match[1]); + const [key, value] = match.slice(1); + const fileMetadataNamesIndex = fileMetadataNames.indexOf(key); if (fileMetadataNamesIndex === -1) { // Check if the match is already existed in the global options - if (globalOptions[match[1]] !== undefined) { - throw new Error("Global Option : '" + match[1] + "' is already existed"); + if (globalOptions[key] !== undefined) { + throw new Error(`Global option '${key}' already exists`); } - globalOptions[match[1]] = match[2]; + globalOptions[key] = value; } else { if (fileMetadataNamesIndex === fileMetadataNames.indexOf(metadataOptionNames.fileName)) { @@ -2416,12 +2383,12 @@ ${code} resetLocalData(); } - currentFileName = basePath + "/" + match[2]; - currentFileOptions[match[1]] = match[2]; + currentFileName = basePath + "/" + value; + currentFileOptions[key] = value; } else { // Add other fileMetadata flag - currentFileOptions[match[1]] = match[2]; + currentFileOptions[key] = value; } } } @@ -2509,7 +2476,7 @@ ${code} } const marker: Marker = { - fileName: fileName, + fileName, position: location.position, data: markerValue }; @@ -2526,7 +2493,7 @@ ${code} function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: MarkerMap, markers: Marker[]): Marker { const marker: Marker = { - fileName: fileName, + fileName, position: location.position }; diff --git a/src/harness/harness.ts b/src/harness/harness.ts index f27e7e1c174..e3bcb001863 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -750,7 +750,7 @@ namespace Harness { export function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]) { const fs = new Utils.VirtualFileSystem(path, useCaseSensitiveFileNames()); - for (const file in listFiles(path)) { + for (const file of listFiles(path)) { fs.addFile(file); } return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), getCurrentDirectory(), path => { @@ -848,9 +848,9 @@ namespace Harness { export const defaultLibFileName = "lib.d.ts"; export const es2015DefaultLibFileName = "lib.es2015.d.ts"; - const libFileNameSourceFileMap: ts.Map = { + const libFileNameSourceFileMap= ts.createMap({ [defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest) - }; + }); export function getDefaultLibrarySourceFile(fileName = defaultLibFileName): ts.SourceFile { if (!isDefaultLibraryFile(fileName)) { @@ -1005,13 +1005,13 @@ namespace Harness { let optionsIndex: ts.Map; function getCommandLineOption(name: string): ts.CommandLineOption { if (!optionsIndex) { - optionsIndex = {}; + optionsIndex = ts.createMap(); const optionDeclarations = harnessOptionDeclarations.concat(ts.optionDeclarations); for (const option of optionDeclarations) { optionsIndex[option.name.toLowerCase()] = option; } } - return ts.lookUp(optionsIndex, name.toLowerCase()); + return optionsIndex[name.toLowerCase()]; } export function setCompilerOptionsFromHarnessSetting(settings: Harness.TestCaseParser.CompilerSettings, options: ts.CompilerOptions & HarnessOptions): void { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index d7ed04b627f..94124432780 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -123,7 +123,7 @@ namespace Harness.LanguageService { } export class LanguageServiceAdapterHost { - protected fileNameToScript: ts.Map = {}; + protected fileNameToScript = ts.createMap(); constructor(protected cancellationToken = DefaultHostCancellationToken.Instance, protected settings = ts.getDefaultCompilerOptions()) { @@ -135,7 +135,7 @@ namespace Harness.LanguageService { public getFilenames(): string[] { const fileNames: string[] = []; - ts.forEachValue(this.fileNameToScript, (scriptInfo) => { + ts.forEachProperty(this.fileNameToScript, (scriptInfo) => { if (scriptInfo.isRootFile) { // only include root files here // usually it means that we won't include lib.d.ts in the list of root files so it won't mess the computation of compilation root dir. @@ -146,7 +146,7 @@ namespace Harness.LanguageService { } public getScriptInfo(fileName: string): ScriptInfo { - return ts.lookUp(this.fileNameToScript, fileName); + return this.fileNameToScript[fileName]; } public addScript(fileName: string, content: string, isRootFile: boolean): void { @@ -235,7 +235,7 @@ namespace Harness.LanguageService { this.getModuleResolutionsForFile = (fileName) => { const scriptInfo = this.getScriptInfo(fileName); const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true); - const imports: ts.Map = {}; + const imports = ts.createMap(); for (const module of preprocessInfo.importedFiles) { const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost); if (resolutionInfo.resolvedModule) { @@ -248,7 +248,7 @@ namespace Harness.LanguageService { const scriptInfo = this.getScriptInfo(fileName); if (scriptInfo) { const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false); - const resolutions: ts.Map = {}; + const resolutions = ts.createMap(); const settings = this.nativeHost.getCompilationSettings(); for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) { const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost); diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 038b2dbe359..76f042834bb 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -253,18 +253,15 @@ class ProjectRunner extends RunnerBase { moduleResolution: ts.ModuleResolutionKind.Classic, // currently all tests use classic module resolution kind, this will change in the future }; // Set the values specified using json - const optionNameMap: ts.Map = {}; - ts.forEach(ts.optionDeclarations, option => { - optionNameMap[option.name] = option; - }); + const optionNameMap = ts.arrayToMap(ts.optionDeclarations, option => option.name); for (const name in testCase) { - if (name !== "mapRoot" && name !== "sourceRoot" && ts.hasProperty(optionNameMap, name)) { + if (name !== "mapRoot" && name !== "sourceRoot" && name in optionNameMap) { const option = optionNameMap[name]; const optType = option.type; let value = testCase[name]; if (typeof optType !== "string") { const key = value.toLowerCase(); - if (ts.hasProperty(optType, key)) { + if (key in optType) { value = optType[key]; } } diff --git a/src/harness/unittests/cachingInServerLSHost.ts b/src/harness/unittests/cachingInServerLSHost.ts index e0ce09391fb..2885b310605 100644 --- a/src/harness/unittests/cachingInServerLSHost.ts +++ b/src/harness/unittests/cachingInServerLSHost.ts @@ -7,16 +7,16 @@ namespace ts { } function createDefaultServerHost(fileMap: Map): server.ServerHost { - const existingDirectories: Map = {}; - forEachValue(fileMap, v => { - let dir = getDirectoryPath(v.name); + const existingDirectories = createMap(); + for (const name in fileMap) { + let dir = getDirectoryPath(name); let previous: string; do { existingDirectories[dir] = true; previous = dir; dir = getDirectoryPath(dir); } while (dir !== previous); - }); + } return { args: [], newLine: "\r\n", @@ -24,7 +24,7 @@ namespace ts { write: (s: string) => { }, readFile: (path: string, encoding?: string): string => { - return hasProperty(fileMap, path) && fileMap[path].content; + return path in fileMap ? fileMap[path].content : undefined; }, writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => { throw new Error("NYI"); @@ -33,10 +33,10 @@ namespace ts { throw new Error("NYI"); }, fileExists: (path: string): boolean => { - return hasProperty(fileMap, path); + return path in fileMap; }, directoryExists: (path: string): boolean => { - return hasProperty(existingDirectories, path); + return existingDirectories[path] || false; }, createDirectory: (path: string) => { }, @@ -101,7 +101,7 @@ namespace ts { content: `foo()` }; - const serverHost = createDefaultServerHost({ [root.name]: root, [imported.name]: imported }); + const serverHost = createDefaultServerHost(createMap({ [root.name]: root, [imported.name]: imported })); const { project, rootScriptInfo } = createProject(root.name, serverHost); // ensure that imported file was found @@ -193,7 +193,7 @@ namespace ts { content: `export var y = 1` }; - const fileMap: Map = { [root.name]: root }; + const fileMap = createMap({ [root.name]: root }); const serverHost = createDefaultServerHost(fileMap); const originalFileExists = serverHost.fileExists; diff --git a/src/harness/unittests/moduleResolution.ts b/src/harness/unittests/moduleResolution.ts index 5f63889b084..09febd2300a 100644 --- a/src/harness/unittests/moduleResolution.ts +++ b/src/harness/unittests/moduleResolution.ts @@ -10,7 +10,7 @@ namespace ts { const map = arrayToMap(files, f => f.name); if (hasDirectoryExists) { - const directories: Map = {}; + const directories = createMap(); for (const f of files) { let name = getDirectoryPath(f.name); while (true) { @@ -25,19 +25,19 @@ namespace ts { return { readFile, directoryExists: path => { - return hasProperty(directories, path); + return path in directories; }, fileExists: path => { - assert.isTrue(hasProperty(directories, getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`); - return hasProperty(map, path); + assert.isTrue(getDirectoryPath(path) in directories, `'fileExists' '${path}' request in non-existing directory`); + return path in map; } }; } else { - return { readFile, fileExists: path => hasProperty(map, path), }; + return { readFile, fileExists: path => path in map, }; } function readFile(path: string): string { - return hasProperty(map, path) ? map[path].content : undefined; + return path in map ? map[path].content : undefined; } } @@ -287,7 +287,7 @@ namespace ts { const host: CompilerHost = { getSourceFile: (fileName: string, languageVersion: ScriptTarget) => { const path = normalizePath(combinePaths(currentDirectory, fileName)); - return hasProperty(files, path) ? createSourceFile(fileName, files[path], languageVersion) : undefined; + return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; }, getDefaultLibFileName: () => "lib.d.ts", writeFile: (fileName, content): void => { throw new Error("NotImplemented"); }, @@ -298,7 +298,7 @@ namespace ts { useCaseSensitiveFileNames: () => false, fileExists: fileName => { const path = normalizePath(combinePaths(currentDirectory, fileName)); - return hasProperty(files, path); + return path in files; }, readFile: (fileName): string => { throw new Error("NotImplemented"); } }; @@ -318,7 +318,7 @@ namespace ts { } it("should find all modules", () => { - const files: Map = { + const files = createMap({ "/a/b/c/first/shared.ts": ` class A {} export = A`, @@ -332,23 +332,23 @@ import Shared = require('../first/shared'); class C {} export = C; ` - }; + }); test(files, "/a/b/c/first/second", ["class_a.ts"], 3, ["../../../c/third/class_c.ts"]); }); it("should find modules in node_modules", () => { - const files: Map = { + const files = createMap({ "/parent/node_modules/mod/index.d.ts": "export var x", "/parent/app/myapp.ts": `import {x} from "mod"` - }; + }); test(files, "/parent/app", ["myapp.ts"], 2, []); }); it("should find file referenced via absolute and relative names", () => { - const files: Map = { + const files = createMap({ "/a/b/c.ts": `/// `, "/a/b/b.ts": "var x" - }; + }); test(files, "/a/b", ["c.ts", "/a/b/b.ts"], 2, []); }); }); @@ -358,11 +358,7 @@ export = C; function test(files: Map, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void { const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); if (!useCaseSensitiveFileNames) { - const f: Map = {}; - for (const fileName in files) { - f[getCanonicalFileName(fileName)] = files[fileName]; - } - files = f; + files = reduceProperties(files, (files, file, fileName) => (files[getCanonicalFileName(fileName)] = file, files), createMap()); } const host: CompilerHost = { @@ -371,7 +367,7 @@ export = C; return library; } const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); - return hasProperty(files, path) ? createSourceFile(fileName, files[path], languageVersion) : undefined; + return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined; }, getDefaultLibFileName: () => "lib.d.ts", writeFile: (fileName, content): void => { throw new Error("NotImplemented"); }, @@ -382,7 +378,7 @@ export = C; useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, fileExists: fileName => { const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName))); - return hasProperty(files, path); + return path in files; }, readFile: (fileName): string => { throw new Error("NotImplemented"); } }; @@ -395,57 +391,57 @@ export = C; } it("should succeed when the same file is referenced using absolute and relative names", () => { - const files: Map = { + const files = createMap({ "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" - }; + }); test(files, { module: ts.ModuleKind.AMD }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "/a/b/d.ts"], []); }); it("should fail when two files used in program differ only in casing (tripleslash references)", () => { - const files: Map = { + const files = createMap({ "/a/b/c.ts": `/// `, "/a/b/d.ts": "var x" - }; + }); test(files, { module: ts.ModuleKind.AMD, forceConsistentCasingInFileNames: true }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "d.ts"], [1149]); }); it("should fail when two files used in program differ only in casing (imports)", () => { - const files: Map = { + const files = createMap({ "/a/b/c.ts": `import {x} from "D"`, "/a/b/d.ts": "export var x" - }; + }); test(files, { module: ts.ModuleKind.AMD, forceConsistentCasingInFileNames: true }, "/a/b", /*useCaseSensitiveFileNames*/ false, ["c.ts", "d.ts"], [1149]); }); it("should fail when two files used in program differ only in casing (imports, relative module names)", () => { - const files: Map = { + const files = createMap({ "moduleA.ts": `import {x} from "./ModuleB"`, "moduleB.ts": "export var x" - }; + }); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "", /*useCaseSensitiveFileNames*/ false, ["moduleA.ts", "moduleB.ts"], [1149]); }); it("should fail when two files exist on disk that differs only in casing", () => { - const files: Map = { + const files = createMap({ "/a/b/c.ts": `import {x} from "D"`, "/a/b/D.ts": "export var x", "/a/b/d.ts": "export var y" - }; + }); test(files, { module: ts.ModuleKind.AMD }, "/a/b", /*useCaseSensitiveFileNames*/ true, ["c.ts", "d.ts"], [1149]); }); it("should fail when module name in 'require' calls has inconsistent casing", () => { - const files: Map = { + const files = createMap({ "moduleA.ts": `import a = require("./ModuleC")`, "moduleB.ts": `import a = require("./moduleC")`, "moduleC.ts": "export var x" - }; + }); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "", /*useCaseSensitiveFileNames*/ false, ["moduleA.ts", "moduleB.ts", "moduleC.ts"], [1149, 1149]); }); it("should fail when module names in 'require' calls has inconsistent casing and current directory has uppercase chars", () => { - const files: Map = { + const files = createMap({ "/a/B/c/moduleA.ts": `import a = require("./ModuleC")`, "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", @@ -453,11 +449,11 @@ export = C; import a = require("./moduleA.ts"); import b = require("./moduleB.ts"); ` - }; + }); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]); }); it("should not fail when module names in 'require' calls has consistent casing and current directory has uppercase chars", () => { - const files: Map = { + const files = createMap({ "/a/B/c/moduleA.ts": `import a = require("./moduleC")`, "/a/B/c/moduleB.ts": `import a = require("./moduleC")`, "/a/B/c/moduleC.ts": "export var x", @@ -465,7 +461,7 @@ import b = require("./moduleB.ts"); import a = require("./moduleA.ts"); import b = require("./moduleB.ts"); ` - }; + }); test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], []); }); }); @@ -1023,7 +1019,7 @@ import b = require("./moduleB.ts"); const names = map(files, f => f.name); const sourceFiles = arrayToMap(map(files, f => createSourceFile(f.name, f.content, ScriptTarget.ES6)), f => f.fileName); const compilerHost: CompilerHost = { - fileExists : fileName => hasProperty(sourceFiles, fileName), + fileExists : fileName => fileName in sourceFiles, getSourceFile: fileName => sourceFiles[fileName], getDefaultLibFileName: () => "lib.d.ts", writeFile(file, text) { @@ -1034,7 +1030,7 @@ import b = require("./moduleB.ts"); getCanonicalFileName: f => f.toLowerCase(), getNewLine: () => "\r\n", useCaseSensitiveFileNames: () => false, - readFile: fileName => hasProperty(sourceFiles, fileName) ? sourceFiles[fileName].text : undefined + readFile: fileName => fileName in sourceFiles ? sourceFiles[fileName].text : undefined }; const program1 = createProgram(names, {}, compilerHost); const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics(); diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 8b2b15c15b3..60687d34c55 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -95,13 +95,14 @@ namespace ts { } } + function createSourceFileWithText(fileName: string, sourceText: SourceText, target: ScriptTarget) { + const file = createSourceFile(fileName, sourceText.getFullText(), target); + file.sourceText = sourceText; + return file; + } + function createTestCompilerHost(texts: NamedSourceText[], target: ScriptTarget): CompilerHost { - const files: Map = {}; - for (const t of texts) { - const file = createSourceFile(t.name, t.text.getFullText(), target); - file.sourceText = t.text; - files[t.name] = file; - } + const files = arrayToMap(texts, t => t.name, t => createSourceFileWithText(t.name, t.text, target)); return { getSourceFile(fileName): SourceFile { @@ -128,10 +129,9 @@ namespace ts { getNewLine(): string { return sys ? sys.newLine : newLine; }, - fileExists: fileName => hasProperty(files, fileName), + fileExists: fileName => fileName in files, readFile: fileName => { - const file = lookUp(files, fileName); - return file && file.text; + return fileName in files ? files[fileName].text : undefined; } }; } @@ -152,29 +152,29 @@ namespace ts { return program; } - function getSizeOfMap(map: Map): number { - let size = 0; - for (const id in map) { - if (hasProperty(map, id)) { - size++; + function checkResolvedModule(expected: ResolvedModule, actual: ResolvedModule): boolean { + if (!expected === !actual) { + if (expected) { + assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); + assert.isTrue(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`); } + return true; } - return size; + return false; } - function checkResolvedModule(expected: ResolvedModule, actual: ResolvedModule): void { - assert.isTrue(actual !== undefined); - assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); - assert.isTrue(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`); + function checkResolvedTypeDirective(expected: ResolvedTypeReferenceDirective, actual: ResolvedTypeReferenceDirective): boolean { + if (!expected === !actual) { + if (expected) { + assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); + assert.isTrue(expected.primary === actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`); + } + return true; + } + return false; } - function checkResolvedTypeDirective(expected: ResolvedTypeReferenceDirective, actual: ResolvedTypeReferenceDirective): void { - assert.isTrue(actual !== undefined); - assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`); - assert.isTrue(expected.primary === actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`); - } - - function checkCache(caption: string, program: Program, fileName: string, expectedContent: Map, getCache: (f: SourceFile) => Map, entryChecker: (expected: T, original: T) => void): void { + function checkCache(caption: string, program: Program, fileName: string, expectedContent: Map, getCache: (f: SourceFile) => Map, entryChecker: (expected: T, original: T) => boolean): void { const file = program.getSourceFile(fileName); assert.isTrue(file !== undefined, `cannot find file ${fileName}`); const cache = getCache(file); @@ -183,23 +183,7 @@ namespace ts { } else { assert.isTrue(cache !== undefined, `expected ${caption} to be set`); - const actualCacheSize = getSizeOfMap(cache); - const expectedSize = getSizeOfMap(expectedContent); - assert.isTrue(actualCacheSize === expectedSize, `expected actual size: ${actualCacheSize} to be equal to ${expectedSize}`); - - for (const id in expectedContent) { - if (hasProperty(expectedContent, id)) { - - if (expectedContent[id]) { - const expected = expectedContent[id]; - const actual = cache[id]; - entryChecker(expected, actual); - } - } - else { - assert.isTrue(cache[id] === undefined); - } - } + assert.isTrue(equalOwnProperties(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`); } } @@ -313,6 +297,8 @@ namespace ts { }); it("resolution cache follows imports", () => { + (Error).stackTraceLimit = Infinity; + const files = [ { name: "a.ts", text: SourceText.New("", "import {_} from 'b'", "var x = 1") }, { name: "b.ts", text: SourceText.New("", "", "var y = 2") }, @@ -320,7 +306,7 @@ namespace ts { const options: CompilerOptions = { target }; const program_1 = newProgram(files, ["a.ts"], options); - checkResolvedModulesCache(program_1, "a.ts", { "b": { resolvedFileName: "b.ts" } }); + checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" } })); checkResolvedModulesCache(program_1, "b.ts", undefined); const program_2 = updateProgram(program_1, ["a.ts"], options, files => { @@ -329,7 +315,7 @@ namespace ts { assert.isTrue(program_1.structureIsReused); // content of resolution cache should not change - checkResolvedModulesCache(program_1, "a.ts", { "b": { resolvedFileName: "b.ts" } }); + checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" } })); checkResolvedModulesCache(program_1, "b.ts", undefined); // imports has changed - program is not reused @@ -346,7 +332,7 @@ namespace ts { files[0].text = files[0].text.updateImportsAndExports(newImports); }); assert.isTrue(!program_3.structureIsReused); - checkResolvedModulesCache(program_4, "a.ts", { "b": { resolvedFileName: "b.ts" }, "c": undefined }); + checkResolvedModulesCache(program_4, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" }, "c": undefined })); }); it("resolved type directives cache follows type directives", () => { @@ -357,7 +343,7 @@ namespace ts { const options: CompilerOptions = { target, typeRoots: ["/types"] }; const program_1 = newProgram(files, ["/a.ts"], options); - checkResolvedTypeDirectivesCache(program_1, "/a.ts", { "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined); const program_2 = updateProgram(program_1, ["/a.ts"], options, files => { @@ -366,7 +352,7 @@ namespace ts { assert.isTrue(program_1.structureIsReused); // content of resolution cache should not change - checkResolvedTypeDirectivesCache(program_1, "/a.ts", { "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined); // type reference directives has changed - program is not reused @@ -384,7 +370,7 @@ namespace ts { files[0].text = files[0].text.updateReferences(newReferences); }); assert.isTrue(!program_3.structureIsReused); - checkResolvedTypeDirectivesCache(program_1, "/a.ts", { "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }); + checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } })); }); }); diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index c5285544329..cf7486acb40 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -106,7 +106,7 @@ namespace ts.server { describe("onMessage", () => { it("should not throw when commands are executed with invalid arguments", () => { let i = 0; - for (name in CommandNames) { + for (const name in CommandNames) { if (!Object.prototype.hasOwnProperty.call(CommandNames, name)) { continue; } @@ -362,13 +362,13 @@ namespace ts.server { class InProcClient { private server: InProcSession; private seq = 0; - private callbacks: ts.Map<(resp: protocol.Response) => void> = {}; - private eventHandlers: ts.Map<(args: any) => void> = {}; + private callbacks = createMap<(resp: protocol.Response) => void>(); + private eventHandlers = createMap<(args: any) => void>(); handle(msg: protocol.Message): void { if (msg.type === "response") { const response = msg; - if (this.callbacks[response.request_seq]) { + if (response.request_seq in this.callbacks) { this.callbacks[response.request_seq](response); delete this.callbacks[response.request_seq]; } @@ -380,7 +380,7 @@ namespace ts.server { } emit(name: string, args: any): void { - if (this.eventHandlers[name]) { + if (name in this.eventHandlers) { this.eventHandlers[name](args); } } diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index 7a375c50b8a..49f033b9def 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -68,20 +68,10 @@ namespace ts { return entry; } - function sizeOfMap(map: Map): number { - let n = 0; - for (const name in map) { - if (hasProperty(map, name)) { - n++; - } - } - return n; - } - function checkMapKeys(caption: string, map: Map, expectedKeys: string[]) { - assert.equal(sizeOfMap(map), expectedKeys.length, `${caption}: incorrect size of map`); + assert.equal(reduceProperties(map, count => count + 1, 0), expectedKeys.length, `${caption}: incorrect size of map`); for (const name of expectedKeys) { - assert.isTrue(hasProperty(map, name), `${caption} is expected to contain ${name}, actual keys: ${getKeys(map)}`); + assert.isTrue(name in map, `${caption} is expected to contain ${name}, actual keys: ${Object.keys(map)}`); } } @@ -126,8 +116,8 @@ namespace ts { private getCanonicalFileName: (s: string) => string; private toPath: (f: string) => Path; private callbackQueue: TimeOutCallback[] = []; - readonly watchedDirectories: Map<{ cb: DirectoryWatcherCallback, recursive: boolean }[]> = {}; - readonly watchedFiles: Map = {}; + readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>(); + readonly watchedFiles = createMap(); constructor(public useCaseSensitiveFileNames: boolean, private executingFilePath: string, private currentDirectory: string, fileOrFolderList: FileOrFolder[]) { this.getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); @@ -208,7 +198,7 @@ namespace ts { watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher { const path = this.toPath(directoryName); - const callbacks = lookUp(this.watchedDirectories, path) || (this.watchedDirectories[path] = []); + const callbacks = this.watchedDirectories[path] || (this.watchedDirectories[path] = []); callbacks.push({ cb: callback, recursive }); return { referenceCount: 0, @@ -229,7 +219,7 @@ namespace ts { triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void { const path = this.toPath(directoryName); - const callbacks = lookUp(this.watchedDirectories, path); + const callbacks = this.watchedDirectories[path]; if (callbacks) { for (const callback of callbacks) { callback.cb(fileName); @@ -239,7 +229,7 @@ namespace ts { triggerFileWatcherCallback(fileName: string, removed?: boolean): void { const path = this.toPath(fileName); - const callbacks = lookUp(this.watchedFiles, path); + const callbacks = this.watchedFiles[path]; if (callbacks) { for (const callback of callbacks) { callback(path, removed); @@ -249,7 +239,7 @@ namespace ts { watchFile(fileName: string, callback: FileWatcherCallback) { const path = this.toPath(fileName); - const callbacks = lookUp(this.watchedFiles, path) || (this.watchedFiles[path] = []); + const callbacks = this.watchedFiles[path] || (this.watchedFiles[path] = []); callbacks.push(callback); return { close: () => { @@ -594,7 +584,7 @@ namespace ts { content: `{ "compilerOptions": { "target": "es6" - }, + }, "files": [ "main.ts" ] }` }; @@ -621,7 +611,7 @@ namespace ts { content: `{ "compilerOptions": { "target": "es6" - }, + }, "files": [ "main.ts" ] }` }; diff --git a/src/lib/es2015.core.d.ts b/src/lib/es2015.core.d.ts index fef0b0ce3ce..15fbb1b77c5 100644 --- a/src/lib/es2015.core.d.ts +++ b/src/lib/es2015.core.d.ts @@ -68,6 +68,10 @@ interface ArrayConstructor { of(...items: T[]): Array; } +interface DateConstructor { + new (value: Date): Date; +} + interface Function { /** * Returns the name of the function. Function names are read-only and can not be changed. @@ -343,6 +347,30 @@ interface ObjectConstructor { defineProperty(o: any, propertyKey: PropertyKey, attributes: PropertyDescriptor): any; } +interface ReadonlyArray { + /** + * Returns the value of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, find + * immediately returns that element value. Otherwise, find returns undefined. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + find(predicate: (value: T, index: number, obj: ReadonlyArray) => boolean, thisArg?: any): T | undefined; + + /** + * Returns the index of the first element in the array where predicate is true, and undefined + * otherwise. + * @param predicate find calls predicate once for each element of the array, in ascending + * order, until it finds one where predicate returns true. If such an element is found, + * findIndex immediately returns that element index. Otherwise, findIndex returns -1. + * @param thisArg If provided, it will be used as the this value for each invocation of + * predicate. If it is not provided, undefined is used instead. + */ + findIndex(predicate: (value: T) => boolean, thisArg?: any): number; +} + interface RegExp { /** * Returns a string indicating the flags of the regular expression in question. This field is read-only. diff --git a/src/lib/es2015.iterable.d.ts b/src/lib/es2015.iterable.d.ts index e1b9d99762b..de339e2bf2c 100644 --- a/src/lib/es2015.iterable.d.ts +++ b/src/lib/es2015.iterable.d.ts @@ -63,6 +63,26 @@ interface ArrayConstructor { from(iterable: Iterable): Array; } +interface ReadonlyArray { + /** Iterator */ + [Symbol.iterator](): IterableIterator; + + /** + * Returns an array of key, value pairs for every entry in the array + */ + entries(): IterableIterator<[number, T]>; + + /** + * Returns an list of keys in the array + */ + keys(): IterableIterator; + + /** + * Returns an list of values in the array + */ + values(): IterableIterator; +} + interface IArguments { /** Iterator */ [Symbol.iterator](): IterableIterator; diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 496df578c19..e4cbe323c68 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1006,7 +1006,12 @@ interface ReadonlyArray { * Combines two or more arrays. * @param items Additional items to add to the end of array1. */ - concat(...items: T[]): T[]; + concat(...items: T[][]): T[]; + /** + * Combines two or more arrays. + * @param items Additional items to add to the end of array1. + */ + concat(...items: (T | T[])[]): T[]; /** * Adds all the elements of an array separated by the specified separator string. * @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma. diff --git a/src/server/client.ts b/src/server/client.ts index f04dbd8dc02..88177e91fad 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -21,7 +21,7 @@ namespace ts.server { export class SessionClient implements LanguageService { private sequence: number = 0; - private lineMaps: ts.Map = {}; + private lineMaps: ts.Map = ts.createMap(); private messages: string[] = []; private lastRenameEntry: RenameEntry; @@ -37,7 +37,7 @@ namespace ts.server { } private getLineMap(fileName: string): number[] { - let lineMap = ts.lookUp(this.lineMaps, fileName); + let lineMap = this.lineMaps[fileName]; if (!lineMap) { const scriptSnapshot = this.host.getScriptSnapshot(fileName); lineMap = this.lineMaps[fileName] = ts.computeLineStarts(scriptSnapshot.getText(0, scriptSnapshot.getLength())); diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 6e9adad7f47..db9bdd5043b 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -130,15 +130,15 @@ namespace ts.server { const path = toPath(containingFile, this.host.getCurrentDirectory(), this.getCanonicalFileName); const currentResolutionsInFile = cache.get(path); - const newResolutions: Map = {}; + const newResolutions = createMap(); const resolvedModules: R[] = []; const compilerOptions = this.getCompilationSettings(); for (const name of names) { // check if this is a duplicate entry in the list - let resolution = lookUp(newResolutions, name); + let resolution = newResolutions[name]; if (!resolution) { - const existingResolution = currentResolutionsInFile && ts.lookUp(currentResolutionsInFile, name); + const existingResolution = currentResolutionsInFile && currentResolutionsInFile[name]; if (moduleResolutionIsValid(existingResolution)) { // ok, it is safe to use existing name resolution results resolution = existingResolution; @@ -378,7 +378,7 @@ namespace ts.server { export interface ProjectOptions { // these fields can be present in the project file files?: string[]; - wildcardDirectories?: ts.Map; + wildcardDirectories?: ts.MapLike; compilerOptions?: ts.CompilerOptions; } @@ -391,7 +391,7 @@ namespace ts.server { // Used to keep track of what directories are watched for this project directoriesWatchedForTsconfig: string[] = []; program: ts.Program; - filenameToSourceFile: ts.Map = {}; + filenameToSourceFile = ts.createMap(); updateGraphSeq = 0; /** Used for configured projects which may have multiple open roots */ openRefCount = 0; @@ -504,7 +504,7 @@ namespace ts.server { return; } - this.filenameToSourceFile = {}; + this.filenameToSourceFile = createMap(); const sourceFiles = this.program.getSourceFiles(); for (let i = 0, len = sourceFiles.length; i < len; i++) { const normFilename = ts.normalizePath(sourceFiles[i].fileName); @@ -563,7 +563,7 @@ namespace ts.server { } let strBuilder = ""; - ts.forEachValue(this.filenameToSourceFile, + ts.forEachProperty(this.filenameToSourceFile, sourceFile => { strBuilder += sourceFile.fileName + "\n"; }); return strBuilder; } @@ -613,7 +613,7 @@ namespace ts.server { } export class ProjectService { - filenameToScriptInfo: ts.Map = {}; + filenameToScriptInfo = ts.createMap(); // open, non-configured root files openFileRoots: ScriptInfo[] = []; // projects built from openFileRoots @@ -625,12 +625,12 @@ namespace ts.server { // open files that are roots of a configured project openFileRootsConfigured: ScriptInfo[] = []; // a path to directory watcher map that detects added tsconfig files - directoryWatchersForTsconfig: ts.Map = {}; + directoryWatchersForTsconfig = ts.createMap(); // count of how many projects are using the directory watcher. If the // number becomes 0 for a watcher, then we should close it. - directoryWatchersRefCount: ts.Map = {}; + directoryWatchersRefCount = ts.createMap(); hostConfiguration: HostConfiguration; - timerForDetectingProjectFileListChanges: Map = {}; + timerForDetectingProjectFileListChanges = createMap(); constructor(public host: ServerHost, public psLogger: Logger, public eventHandler?: ProjectServiceEventHandler) { // ts.disableIncrementalParsing = true; @@ -857,7 +857,7 @@ namespace ts.server { if (project.isConfiguredProject()) { project.projectFileWatcher.close(); project.directoryWatcher.close(); - forEachValue(project.directoriesWatchedForWildcards, watcher => { watcher.close(); }); + forEachProperty(project.directoriesWatchedForWildcards, watcher => { watcher.close(); }); delete project.directoriesWatchedForWildcards; this.configuredProjects = copyListRemovingItem(project, this.configuredProjects); } @@ -1124,7 +1124,7 @@ namespace ts.server { getScriptInfo(filename: string) { filename = ts.normalizePath(filename); - return ts.lookUp(this.filenameToScriptInfo, filename); + return this.filenameToScriptInfo[filename]; } /** @@ -1133,7 +1133,7 @@ namespace ts.server { */ openFile(fileName: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind) { fileName = ts.normalizePath(fileName); - let info = ts.lookUp(this.filenameToScriptInfo, fileName); + let info = this.filenameToScriptInfo[fileName]; if (!info) { let content: string; if (this.host.fileExists(fileName)) { @@ -1246,7 +1246,7 @@ namespace ts.server { * @param filename is absolute pathname */ closeClientFile(filename: string) { - const info = ts.lookUp(this.filenameToScriptInfo, filename); + const info = this.filenameToScriptInfo[filename]; if (info) { this.closeOpenFile(info); info.isOpen = false; @@ -1255,14 +1255,14 @@ namespace ts.server { } getProjectForFile(filename: string) { - const scriptInfo = ts.lookUp(this.filenameToScriptInfo, filename); + const scriptInfo = this.filenameToScriptInfo[filename]; if (scriptInfo) { return scriptInfo.defaultProject; } } printProjectsForFile(filename: string) { - const scriptInfo = ts.lookUp(this.filenameToScriptInfo, filename); + const scriptInfo = this.filenameToScriptInfo[filename]; if (scriptInfo) { this.psLogger.startGroup(); this.psLogger.info("Projects for " + filename); @@ -1419,7 +1419,7 @@ namespace ts.server { /*recursive*/ true ); - project.directoriesWatchedForWildcards = reduceProperties(projectOptions.wildcardDirectories, (watchers, flag, directory) => { + project.directoriesWatchedForWildcards = reduceProperties(createMap(projectOptions.wildcardDirectories), (watchers, flag, directory) => { if (comparePaths(configDirectoryPath, directory, ".", !this.host.useCaseSensitiveFileNames) !== Comparison.EqualTo) { const recursive = (flag & WatchDirectoryFlags.Recursive) !== 0; this.log(`Add ${ recursive ? "recursive " : ""}watcher for: ${directory}`); diff --git a/src/server/session.ts b/src/server/session.ts index 7e1ca81e2af..9383a54bd48 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1061,7 +1061,7 @@ namespace ts.server { return { response, responseRequired: true }; } - private handlers: Map<(request: protocol.Request) => { response?: any, responseRequired?: boolean }> = { + private handlers = createMap<(request: protocol.Request) => { response?: any, responseRequired?: boolean }>({ [CommandNames.Exit]: () => { this.exit(); return { responseRequired: false }; @@ -1198,9 +1198,10 @@ namespace ts.server { this.reloadProjects(); return { responseRequired: false }; } - }; + }); + public addProtocolHandler(command: string, handler: (request: protocol.Request) => { response?: any, responseRequired: boolean }) { - if (this.handlers[command]) { + if (command in this.handlers) { throw new Error(`Protocol handler already exists for command "${command}"`); } this.handlers[command] = handler; diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json index 746283720bf..af04a74d557 100644 --- a/src/server/tsconfig.library.json +++ b/src/server/tsconfig.library.json @@ -10,6 +10,8 @@ "types": [] }, "files": [ + "../services/shims.ts", + "../services/utilities.ts", "editorServices.ts", "protocol.d.ts", "session.ts" diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 3b013a4a924..4f0e06244d2 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -47,7 +47,7 @@ namespace ts.JsTyping { { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { // A typing name to typing file path mapping - const inferredTypings: Map = {}; + const inferredTypings = createMap(); if (!typingOptions || !typingOptions.enableAutoDiscovery) { return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; @@ -58,12 +58,7 @@ namespace ts.JsTyping { if (!safeList) { const result = readConfigFile(safeListPath, (path: string) => host.readFile(path)); - if (result.config) { - safeList = result.config; - } - else { - safeList = {}; - }; + safeList = createMap(result.config); } const filesToWatch: string[] = []; @@ -93,7 +88,7 @@ namespace ts.JsTyping { // Add the cached typing locations for inferred typings that are already installed for (const name in packageNameToTypingLocation) { - if (hasProperty(inferredTypings, name) && !inferredTypings[name]) { + if (name in inferredTypings && !inferredTypings[name]) { inferredTypings[name] = packageNameToTypingLocation[name]; } } @@ -124,7 +119,7 @@ namespace ts.JsTyping { } for (const typing of typingNames) { - if (!hasProperty(inferredTypings, typing)) { + if (!(typing in inferredTypings)) { inferredTypings[typing] = undefined; } } @@ -139,16 +134,16 @@ namespace ts.JsTyping { const jsonConfig: PackageJson = result.config; filesToWatch.push(jsonPath); if (jsonConfig.dependencies) { - mergeTypings(getKeys(jsonConfig.dependencies)); + mergeTypings(getOwnKeys(jsonConfig.dependencies)); } if (jsonConfig.devDependencies) { - mergeTypings(getKeys(jsonConfig.devDependencies)); + mergeTypings(getOwnKeys(jsonConfig.devDependencies)); } if (jsonConfig.optionalDependencies) { - mergeTypings(getKeys(jsonConfig.optionalDependencies)); + mergeTypings(getOwnKeys(jsonConfig.optionalDependencies)); } if (jsonConfig.peerDependencies) { - mergeTypings(getKeys(jsonConfig.peerDependencies)); + mergeTypings(getOwnKeys(jsonConfig.peerDependencies)); } } } @@ -167,7 +162,7 @@ namespace ts.JsTyping { mergeTypings(cleanedTypingNames); } else { - mergeTypings(filter(cleanedTypingNames, f => hasProperty(safeList, f))); + mergeTypings(filter(cleanedTypingNames, f => f in safeList)); } const hasJsxFile = forEach(fileNames, f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX)); diff --git a/src/services/navigateTo.ts b/src/services/navigateTo.ts index e9afb6925b5..ccded188e61 100644 --- a/src/services/navigateTo.ts +++ b/src/services/navigateTo.ts @@ -15,7 +15,7 @@ namespace ts.NavigateTo { const nameToDeclarations = sourceFile.getNamedDeclarations(); for (const name in nameToDeclarations) { - const declarations = getProperty(nameToDeclarations, name); + const declarations = nameToDeclarations[name]; if (declarations) { // First do a quick check to see if the name of the declaration matches the // last portion of the (possibly) dotted name they're searching for. diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts index f105a9fa6e2..dda9ef485d2 100644 --- a/src/services/navigationBar.ts +++ b/src/services/navigationBar.ts @@ -234,7 +234,7 @@ namespace ts.NavigationBar { /** Merge declarations of the same kind. */ function mergeChildren(children: NavigationBarNode[]): void { - const nameToItems: Map = {}; + const nameToItems = createMap(); filterMutate(children, child => { const decl = child.node; const name = decl.name && nodeText(decl.name); @@ -243,7 +243,7 @@ namespace ts.NavigationBar { return true; } - const itemsWithSameName = getProperty(nameToItems, name); + const itemsWithSameName = nameToItems[name]; if (!itemsWithSameName) { nameToItems[name] = child; return true; diff --git a/src/services/patternMatcher.ts b/src/services/patternMatcher.ts index 3d20337eca7..b4f67ca056d 100644 --- a/src/services/patternMatcher.ts +++ b/src/services/patternMatcher.ts @@ -113,7 +113,7 @@ namespace ts { // we see the name of a module that is used everywhere, or the name of an overload). As // such, we cache the information we compute about the candidate for the life of this // pattern matcher so we don't have to compute it multiple times. - const stringToWordSpans: Map = {}; + const stringToWordSpans = createMap(); pattern = pattern.trim(); @@ -188,7 +188,7 @@ namespace ts { } function getWordSpans(word: string): TextSpan[] { - if (!hasProperty(stringToWordSpans, word)) { + if (!(word in stringToWordSpans)) { stringToWordSpans[word] = breakIntoWordSpans(word); } diff --git a/src/services/services.ts b/src/services/services.ts index 6ec95343e33..148d3af427c 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -975,7 +975,7 @@ namespace ts { } private computeNamedDeclarations(): Map { - const result: Map = {}; + const result = createMap(); forEachChild(this, visit); @@ -990,7 +990,7 @@ namespace ts { } function getDeclarations(name: string) { - return getProperty(result, name) || (result[name] = []); + return result[name] || (result[name] = []); } function getDeclarationName(declaration: Declaration) { @@ -1886,7 +1886,7 @@ namespace ts { }; } - // Cache host information about scrip Should be refreshed + // Cache host information about script should be refreshed // at each language service public entry point, since we don't know when // set of scripts handled by the host changes. class HostCache { @@ -2025,7 +2025,7 @@ namespace ts { fileName?: string; reportDiagnostics?: boolean; moduleName?: string; - renamedDependencies?: Map; + renamedDependencies?: MapLike; } export interface TranspileOutput { @@ -2042,7 +2042,7 @@ namespace ts { function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { // Lazily create this value to fix module loading errors. commandLineOptionsStringToEnum = commandLineOptionsStringToEnum || filter(optionDeclarations, o => - typeof o.type === "object" && !forEachValue(>o.type, v => typeof v !== "number")); + typeof o.type === "object" && !forEachProperty(o.type, v => typeof v !== "number")); options = clone(options); @@ -2058,7 +2058,7 @@ namespace ts { options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); } else { - if (!forEachValue(opt.type, v => v === value)) { + if (!forEachProperty(opt.type, v => v === value)) { // Supplied value isn't a valid enum value. diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); } @@ -2117,7 +2117,9 @@ namespace ts { sourceFile.moduleName = transpileOptions.moduleName; } - sourceFile.renamedDependencies = transpileOptions.renamedDependencies; + if (transpileOptions.renamedDependencies) { + sourceFile.renamedDependencies = createMap(transpileOptions.renamedDependencies); + } const newLine = getNewLineCharacter(options); @@ -2243,7 +2245,7 @@ namespace ts { export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry { // Maps from compiler setting target (ES3, ES5, etc.) to all the cached documents we have // for those settings. - const buckets: Map> = {}; + const buckets = createMap>(); const getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames); function getKeyForCompilationSettings(settings: CompilerOptions): DocumentRegistryBucketKey { @@ -2251,7 +2253,7 @@ namespace ts { } function getBucketForCompilationSettings(key: DocumentRegistryBucketKey, createIfMissing: boolean): FileMap { - let bucket = lookUp(buckets, key); + let bucket = buckets[key]; if (!bucket && createIfMissing) { buckets[key] = bucket = createFileMap(); } @@ -2260,7 +2262,7 @@ namespace ts { function reportStats() { const bucketInfoArray = Object.keys(buckets).filter(name => name && name.charAt(0) === "_").map(name => { - const entries = lookUp(buckets, name); + const entries = buckets[name]; const sourceFiles: { name: string; refCount: number; references: string[]; }[] = []; entries.forEachValue((key, entry) => { sourceFiles.push({ @@ -3099,7 +3101,7 @@ namespace ts { oldSettings.allowJs !== newSettings.allowJs || oldSettings.disableSizeLimit !== oldSettings.disableSizeLimit || oldSettings.baseUrl !== newSettings.baseUrl || - !mapIsEqualTo(oldSettings.paths, newSettings.paths)); + !equalOwnProperties(oldSettings.paths, newSettings.paths)); // Now create a new compiler const compilerHost: CompilerHost = { @@ -3114,7 +3116,6 @@ namespace ts { getCurrentDirectory: () => currentDirectory, fileExists: (fileName): boolean => { // stub missing host functionality - Debug.assert(!host.resolveModuleNames || !host.resolveTypeReferenceDirectives); return hostCache.getOrCreateEntry(fileName) !== undefined; }, readFile: (fileName): string => { @@ -4102,7 +4103,7 @@ namespace ts { * do not occur at the current position and have not otherwise been typed. */ function filterNamedImportOrExportCompletionItems(exportsOfModule: Symbol[], namedImportsOrExports: ImportOrExportSpecifier[]): Symbol[] { - const existingImportsOrExports: Map = {}; + const existingImportsOrExports = createMap(); for (const element of namedImportsOrExports) { // If this is the current item we are editing right now, do not filter it out @@ -4114,11 +4115,11 @@ namespace ts { existingImportsOrExports[name.text] = true; } - if (isEmpty(existingImportsOrExports)) { + if (!someProperties(existingImportsOrExports)) { return filter(exportsOfModule, e => e.name !== "default"); } - return filter(exportsOfModule, e => e.name !== "default" && !lookUp(existingImportsOrExports, e.name)); + return filter(exportsOfModule, e => e.name !== "default" && !existingImportsOrExports[e.name]); } /** @@ -4132,7 +4133,7 @@ namespace ts { return contextualMemberSymbols; } - const existingMemberNames: Map = {}; + const existingMemberNames = createMap(); for (const m of existingMembers) { // Ignore omitted expressions for missing members if (m.kind !== SyntaxKind.PropertyAssignment && @@ -4165,7 +4166,7 @@ namespace ts { existingMemberNames[existingName] = true; } - return filter(contextualMemberSymbols, m => !lookUp(existingMemberNames, m.name)); + return filter(contextualMemberSymbols, m => !existingMemberNames[m.name]); } /** @@ -4175,7 +4176,7 @@ namespace ts { * do not occur at the current position and have not otherwise been typed. */ function filterJsxAttributes(symbols: Symbol[], attributes: NodeArray): Symbol[] { - const seenNames: Map = {}; + const seenNames = createMap(); for (const attr of attributes) { // If this is the current item we are editing right now, do not filter it out if (attr.getStart() <= position && position <= attr.getEnd()) { @@ -4187,7 +4188,7 @@ namespace ts { } } - return filter(symbols, a => !lookUp(seenNames, a.name)); + return filter(symbols, a => !seenNames[a.name]); } } @@ -4317,13 +4318,13 @@ namespace ts { function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[], location: Node, performCharacterChecks: boolean): Map { const start = timestamp(); - const uniqueNames: Map = {}; + const uniqueNames = createMap(); if (symbols) { for (const symbol of symbols) { const entry = createCompletionEntry(symbol, location, performCharacterChecks); if (entry) { const id = escapeIdentifier(entry.name); - if (!lookUp(uniqueNames, id)) { + if (!uniqueNames[id]) { entries.push(entry); uniqueNames[id] = id; } @@ -4830,7 +4831,14 @@ namespace ts { } if (symbolFlags & SymbolFlags.Alias) { addNewLineIfDisplayPartsExist(); - displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); + if (symbol.declarations[0].kind === SyntaxKind.NamespaceExportDeclaration) { + displayParts.push(keywordPart(SyntaxKind.ExportKeyword)); + displayParts.push(spacePart()); + displayParts.push(keywordPart(SyntaxKind.NamespaceKeyword)); + } + else { + displayParts.push(keywordPart(SyntaxKind.ImportKeyword)); + } displayParts.push(spacePart()); addFullSymbolName(symbol); ts.forEach(symbol.declarations, declaration => { @@ -5144,7 +5152,7 @@ namespace ts { // Type reference directives const typeReferenceDirective = findReferenceInPosition(sourceFile.typeReferenceDirectives, position); if (typeReferenceDirective) { - const referenceFile = lookUp(program.getResolvedTypeReferenceDirectives(), typeReferenceDirective.fileName); + const referenceFile = program.getResolvedTypeReferenceDirectives()[typeReferenceDirective.fileName]; if (referenceFile && referenceFile.resolvedFileName) { return [getDefinitionInfoForFileReference(typeReferenceDirective.fileName, referenceFile.resolvedFileName)]; } @@ -5311,12 +5319,12 @@ namespace ts { return undefined; } - const fileNameToDocumentHighlights: Map = {}; + const fileNameToDocumentHighlights = createMap(); const result: DocumentHighlights[] = []; for (const referencedSymbol of referencedSymbols) { for (const referenceEntry of referencedSymbol.references) { const fileName = referenceEntry.fileName; - let documentHighlights = getProperty(fileNameToDocumentHighlights, fileName); + let documentHighlights = fileNameToDocumentHighlights[fileName]; if (!documentHighlights) { documentHighlights = { fileName, highlightSpans: [] }; @@ -6061,7 +6069,7 @@ namespace ts { const nameTable = getNameTable(sourceFile); - if (lookUp(nameTable, internedName) !== undefined) { + if (nameTable[internedName] !== undefined) { result = result || []; getReferencesInNode(sourceFile, symbol, declaredName, node, searchMeaning, findInStrings, findInComments, result, symbolToIndex); } @@ -6706,7 +6714,7 @@ namespace ts { // Add symbol of properties/methods of the same name in base classes and implemented interfaces definitions if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ {}); + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ createMap()); } }); @@ -6738,7 +6746,7 @@ namespace ts { // the function will add any found symbol of the property-name, then its sub-routine will call // getPropertySymbolsFromBaseTypes again to walk up any base types to prevent revisiting already // visited symbol, interface "C", the sub-routine will pass the current symbol as previousIterationSymbol. - if (hasProperty(previousIterationSymbolsCache, symbol.name)) { + if (symbol.name in previousIterationSymbolsCache) { return; } @@ -6827,7 +6835,7 @@ namespace ts { // see if any is in the list if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { const result: Symbol[] = []; - getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ {}); + getPropertySymbolsFromBaseTypes(rootSymbol.parent, rootSymbol.getName(), result, /*previousIterationSymbolsCache*/ createMap()); return forEach(result, s => searchSymbols.indexOf(s) >= 0 ? s : undefined); } @@ -8311,7 +8319,7 @@ namespace ts { } function initializeNameTable(sourceFile: SourceFile): void { - const nameTable: Map = {}; + const nameTable = createMap(); walk(sourceFile); sourceFile.nameTable = nameTable; diff --git a/src/services/shims.ts b/src/services/shims.ts index 271d6f2d6e2..ff57dd9cf7a 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -72,8 +72,13 @@ namespace ts { directoryExists(directoryName: string): boolean; } - /** Public interface of the the of a config service shim instance.*/ - export interface CoreServicesShimHost extends Logger, ModuleResolutionHost { + /** Public interface of the core-services host instance used in managed side */ + export interface CoreServicesShimHost extends Logger { + directoryExists(directoryName: string): boolean; + fileExists(fileName: string): boolean; + getCurrentDirectory(): string; + getDirectories(path: string): string; + /** * Returns a JSON-encoded value of the type: string[] * @@ -81,9 +86,14 @@ namespace ts { * when enumerating the directory. */ readDirectory(rootDir: string, extension: string, basePaths?: string, excludeEx?: string, includeFileEx?: string, includeDirEx?: string, depth?: number): string; - useCaseSensitiveFileNames?(): boolean; - getCurrentDirectory(): string; + + /** + * Read arbitary text files on disk, i.e. when resolution procedure needs the content of 'package.json' to determine location of bundled typings for node modules + */ + readFile(fileName: string): string; + realpath?(path: string): string; trace(s: string): void; + useCaseSensitiveFileNames?(): boolean; } /// @@ -240,6 +250,7 @@ namespace ts { } export interface CoreServicesShim extends Shim { + getAutomaticTypeDirectiveNames(compilerOptionsJson: string): string; getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getDefaultCompilationSettings(): string; @@ -300,9 +311,9 @@ namespace ts { // 'in' does not have this effect. if ("getModuleResolutionsForFile" in this.shimHost) { this.resolveModuleNames = (moduleNames: string[], containingFile: string) => { - const resolutionsInFile = >JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile)); + const resolutionsInFile = >JSON.parse(this.shimHost.getModuleResolutionsForFile(containingFile)); return map(moduleNames, name => { - const result = lookUp(resolutionsInFile, name); + const result = getProperty(resolutionsInFile, name); return result ? { resolvedFileName: result } : undefined; }); }; @@ -312,8 +323,8 @@ namespace ts { } if ("getTypeReferenceDirectiveResolutionsForFile" in this.shimHost) { this.resolveTypeReferenceDirectives = (typeDirectiveNames: string[], containingFile: string) => { - const typeDirectivesForFile = >JSON.parse(this.shimHost.getTypeReferenceDirectiveResolutionsForFile(containingFile)); - return map(typeDirectiveNames, name => lookUp(typeDirectivesForFile, name)); + const typeDirectivesForFile = >JSON.parse(this.shimHost.getTypeReferenceDirectiveResolutionsForFile(containingFile)); + return map(typeDirectiveNames, name => getProperty(typeDirectivesForFile, name)); }; } } @@ -492,6 +503,10 @@ namespace ts { private readDirectoryFallback(rootDir: string, extension: string, exclude: string[]) { return JSON.parse(this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude))); } + + public getDirectories(path: string): string[] { + return JSON.parse(this.shimHost.getDirectories(path)); + } } function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): any { @@ -1003,7 +1018,7 @@ namespace ts { public getPreProcessedFileInfo(fileName: string, sourceTextSnapshot: IScriptSnapshot): string { return this.forwardJSONCall( - "getPreProcessedFileInfo('" + fileName + "')", + `getPreProcessedFileInfo('${fileName}')`, () => { // for now treat files as JavaScript const result = preProcessFile(sourceTextSnapshot.getText(0, sourceTextSnapshot.getLength()), /* readImportFiles */ true, /* detectJavaScriptImports */ true); @@ -1017,6 +1032,16 @@ namespace ts { }); } + public getAutomaticTypeDirectiveNames(compilerOptionsJson: string): string { + return this.forwardJSONCall( + `getAutomaticTypeDirectiveNames('${compilerOptionsJson}')`, + () => { + const compilerOptions = JSON.parse(compilerOptionsJson); + return getAutomaticTypeDirectiveNames(compilerOptions, this.host); + } + ); + } + private convertFileReferences(refs: FileReference[]): IFileReference[] { if (!refs) { return undefined; diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 50378aa64b1..211e55b23ba 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -237,7 +237,7 @@ namespace ts.SignatureHelp { const typeChecker = program.getTypeChecker(); for (const sourceFile of program.getSourceFiles()) { const nameToDeclarations = sourceFile.getNamedDeclarations(); - const declarations = getProperty(nameToDeclarations, name.text); + const declarations = nameToDeclarations[name.text]; if (declarations) { for (const declaration of declarations) { diff --git a/tests/baselines/reference/APISample_watcher.js b/tests/baselines/reference/APISample_watcher.js index 7573f2ba99e..cb7fc3a4c5c 100644 --- a/tests/baselines/reference/APISample_watcher.js +++ b/tests/baselines/reference/APISample_watcher.js @@ -20,7 +20,7 @@ declare var path: any; import * as ts from "typescript"; function watch(rootFileNames: string[], options: ts.CompilerOptions) { - const files: ts.Map<{ version: number }> = {}; + const files: ts.MapLike<{ version: number }> = {}; // initialize the list of files rootFileNames.forEach(fileName => { diff --git a/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt new file mode 100644 index 00000000000..6f4a0ef9d6d --- /dev/null +++ b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt @@ -0,0 +1,46 @@ +tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(13,1): error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray'. + Types of property 'concat' are incompatible. + Type '{ (...items: A[][]): A[]; (...items: (A | A[])[]): A[]; }' is not assignable to type '{ >(...items: U[]): B[]; (...items: B[][]): B[]; (...items: (B | B[])[]): B[]; }'. + Type 'A[]' is not assignable to type 'B[]'. + Type 'A' is not assignable to type 'B'. + Property 'b' is missing in type 'A'. +tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error TS2322: Type 'C' is not assignable to type 'ReadonlyArray'. + Types of property 'concat' are incompatible. + Type '{ (...items: A[][]): A[]; (...items: (A | A[])[]): A[]; }' is not assignable to type '{ >(...items: U[]): B[]; (...items: B[][]): B[]; (...items: (B | B[])[]): B[]; }'. + Type 'A[]' is not assignable to type 'B[]'. + Type 'A' is not assignable to type 'B'. + + +==== tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts (2 errors) ==== + class A { a } + class B extends A { b } + class C extends Array { c } + declare var ara: A[]; + declare var arb: B[]; + declare var cra: C; + declare var crb: C; + declare var rra: ReadonlyArray; + declare var rrb: ReadonlyArray; + rra = ara; + rrb = arb; // OK, Array is assignable to ReadonlyArray + rra = arb; + rrb = ara; // error: 'A' is not assignable to 'B' + ~~~ +!!! error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray'. +!!! error TS2322: Types of property 'concat' are incompatible. +!!! error TS2322: Type '{ (...items: A[][]): A[]; (...items: (A | A[])[]): A[]; }' is not assignable to type '{ >(...items: U[]): B[]; (...items: B[][]): B[]; (...items: (B | B[])[]): B[]; }'. +!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'. +!!! error TS2322: Type 'A' is not assignable to type 'B'. +!!! error TS2322: Property 'b' is missing in type 'A'. + + rra = cra; + rra = crb; // OK, C is assignable to ReadonlyArray + rrb = crb; + rrb = cra; // error: 'A' is not assignable to 'B' + ~~~ +!!! error TS2322: Type 'C' is not assignable to type 'ReadonlyArray'. +!!! error TS2322: Types of property 'concat' are incompatible. +!!! error TS2322: Type '{ (...items: A[][]): A[]; (...items: (A | A[])[]): A[]; }' is not assignable to type '{ >(...items: U[]): B[]; (...items: B[][]): B[]; (...items: (B | B[])[]): B[]; }'. +!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'. +!!! error TS2322: Type 'A' is not assignable to type 'B'. + \ No newline at end of file diff --git a/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.js b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.js new file mode 100644 index 00000000000..255b52e16c9 --- /dev/null +++ b/tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.js @@ -0,0 +1,54 @@ +//// [arrayOfSubtypeIsAssignableToReadonlyArray.ts] +class A { a } +class B extends A { b } +class C extends Array { c } +declare var ara: A[]; +declare var arb: B[]; +declare var cra: C; +declare var crb: C; +declare var rra: ReadonlyArray; +declare var rrb: ReadonlyArray; +rra = ara; +rrb = arb; // OK, Array is assignable to ReadonlyArray +rra = arb; +rrb = ara; // error: 'A' is not assignable to 'B' + +rra = cra; +rra = crb; // OK, C is assignable to ReadonlyArray +rrb = crb; +rrb = cra; // error: 'A' is not assignable to 'B' + + +//// [arrayOfSubtypeIsAssignableToReadonlyArray.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var A = (function () { + function A() { + } + return A; +}()); +var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; +}(A)); +var C = (function (_super) { + __extends(C, _super); + function C() { + _super.apply(this, arguments); + } + return C; +}(Array)); +rra = ara; +rrb = arb; // OK, Array is assignable to ReadonlyArray +rra = arb; +rrb = ara; // error: 'A' is not assignable to 'B' +rra = cra; +rra = crb; // OK, C is assignable to ReadonlyArray +rrb = crb; +rrb = cra; // error: 'A' is not assignable to 'B' diff --git a/tests/baselines/reference/classExpressionES63.js b/tests/baselines/reference/classExpressionES63.js index 6b3a06cf7c3..5a72e173475 100644 --- a/tests/baselines/reference/classExpressionES63.js +++ b/tests/baselines/reference/classExpressionES63.js @@ -13,14 +13,14 @@ let C = class extends class extends class { } } { - constructor(...args) { - super(...args); + constructor() { + super(...arguments); this.b = 2; } } { - constructor(...args) { - super(...args); + constructor() { + super(...arguments); this.c = 3; } } diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols index e76cfbf4cab..217e11dc95d 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.symbols +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.symbols @@ -64,9 +64,9 @@ if (isNodeList(sourceObj)) { >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) sourceObj.length; ->sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj.length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) ->length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>length : Symbol(NodeList.length, Decl(controlFlowBinaryOrExpression.ts, 10, 27)) } if (isHTMLCollection(sourceObj)) { @@ -74,9 +74,9 @@ if (isHTMLCollection(sourceObj)) { >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) sourceObj.length; ->sourceObj.length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>sourceObj.length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) >sourceObj : Symbol(sourceObj, Decl(controlFlowBinaryOrExpression.ts, 23, 3)) ->length : Symbol(length, Decl(controlFlowBinaryOrExpression.ts, 10, 27), Decl(controlFlowBinaryOrExpression.ts, 14, 33)) +>length : Symbol(HTMLCollection.length, Decl(controlFlowBinaryOrExpression.ts, 14, 33)) } if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { diff --git a/tests/baselines/reference/controlFlowBinaryOrExpression.types b/tests/baselines/reference/controlFlowBinaryOrExpression.types index 0bf5ab524b9..7e92fcdbbd1 100644 --- a/tests/baselines/reference/controlFlowBinaryOrExpression.types +++ b/tests/baselines/reference/controlFlowBinaryOrExpression.types @@ -80,7 +80,7 @@ if (isNodeList(sourceObj)) { sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList | HTMLCollection +>sourceObj : NodeList >length : number } @@ -91,7 +91,7 @@ if (isHTMLCollection(sourceObj)) { sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList | HTMLCollection +>sourceObj : HTMLCollection >length : number } @@ -102,11 +102,11 @@ if (isNodeList(sourceObj) || isHTMLCollection(sourceObj)) { >sourceObj : EventTargetLike >isHTMLCollection(sourceObj) : boolean >isHTMLCollection : (sourceObj: any) => sourceObj is HTMLCollection ->sourceObj : { a: string; } +>sourceObj : HTMLCollection | { a: string; } sourceObj.length; >sourceObj.length : number ->sourceObj : NodeList | HTMLCollection | ({ a: string; } & HTMLCollection) +>sourceObj : NodeList | HTMLCollection >length : number } diff --git a/tests/baselines/reference/controlFlowIfStatement.js b/tests/baselines/reference/controlFlowIfStatement.js index a70c07d38dc..e40b831552f 100644 --- a/tests/baselines/reference/controlFlowIfStatement.js +++ b/tests/baselines/reference/controlFlowIfStatement.js @@ -35,6 +35,22 @@ function b() { } x; // string } +function c(data: string | T): T { + if (typeof data === 'string') { + return JSON.parse(data); + } + else { + return data; + } +} +function d(data: string | T): never { + if (typeof data === 'string') { + throw new Error('will always happen'); + } + else { + return data; + } +} //// [controlFlowIfStatement.js] @@ -72,3 +88,19 @@ function b() { } x; // string } +function c(data) { + if (typeof data === 'string') { + return JSON.parse(data); + } + else { + return data; + } +} +function d(data) { + if (typeof data === 'string') { + throw new Error('will always happen'); + } + else { + return data; + } +} diff --git a/tests/baselines/reference/controlFlowIfStatement.symbols b/tests/baselines/reference/controlFlowIfStatement.symbols index 1d85b31c998..e4d2bb9f184 100644 --- a/tests/baselines/reference/controlFlowIfStatement.symbols +++ b/tests/baselines/reference/controlFlowIfStatement.symbols @@ -71,4 +71,42 @@ function b() { x; // string >x : Symbol(x, Decl(controlFlowIfStatement.ts, 26, 7)) } +function c(data: string | T): T { +>c : Symbol(c, Decl(controlFlowIfStatement.ts, 35, 1)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11)) +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 36, 11)) + + if (typeof data === 'string') { +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14)) + + return JSON.parse(data); +>JSON.parse : Symbol(JSON.parse, Decl(lib.d.ts, --, --)) +>JSON : Symbol(JSON, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +>parse : Symbol(JSON.parse, Decl(lib.d.ts, --, --)) +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14)) + } + else { + return data; +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 36, 14)) + } +} +function d(data: string | T): never { +>d : Symbol(d, Decl(controlFlowIfStatement.ts, 43, 1)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 44, 11)) +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29)) +>T : Symbol(T, Decl(controlFlowIfStatement.ts, 44, 11)) + + if (typeof data === 'string') { +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29)) + + throw new Error('will always happen'); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + } + else { + return data; +>data : Symbol(data, Decl(controlFlowIfStatement.ts, 44, 29)) + } +} diff --git a/tests/baselines/reference/controlFlowIfStatement.types b/tests/baselines/reference/controlFlowIfStatement.types index 716491ea58b..79985378bf5 100644 --- a/tests/baselines/reference/controlFlowIfStatement.types +++ b/tests/baselines/reference/controlFlowIfStatement.types @@ -90,4 +90,51 @@ function b() { x; // string >x : string } +function c(data: string | T): T { +>c : (data: string | T) => T +>T : T +>data : string | T +>T : T +>T : T + + if (typeof data === 'string') { +>typeof data === 'string' : boolean +>typeof data : string +>data : string | T +>'string' : "string" + + return JSON.parse(data); +>JSON.parse(data) : any +>JSON.parse : (text: string, reviver?: (key: any, value: any) => any) => any +>JSON : JSON +>parse : (text: string, reviver?: (key: any, value: any) => any) => any +>data : string + } + else { + return data; +>data : T + } +} +function d(data: string | T): never { +>d : (data: string | T) => never +>T : T +>data : string | T +>T : T + + if (typeof data === 'string') { +>typeof data === 'string' : boolean +>typeof data : string +>data : string | T +>'string' : "string" + + throw new Error('will always happen'); +>new Error('will always happen') : Error +>Error : ErrorConstructor +>'will always happen' : string + } + else { + return data; +>data : never + } +} diff --git a/tests/baselines/reference/controlFlowInstanceof.js b/tests/baselines/reference/controlFlowInstanceof.js new file mode 100644 index 00000000000..3f2287b4628 --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceof.js @@ -0,0 +1,184 @@ +//// [controlFlowInstanceof.ts] + +// Repros from #10167 + +function f1(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + s; // Set + s.add(42); +} + +function f2(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Promise) { + s; // Set & Promise + } + s; // Set + s.add(42); +} + +function f3(s: Set | Set) { + s; // Set | Set + if (s instanceof Set) { + s; // Set | Set + } + else { + s; // never + } +} + +function f4(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + else { + s; // never + } +} + +// More tests + +class A { a: string } +class B extends A { b: string } +class C extends A { c: string } + +function foo(x: A | undefined) { + x; // A | undefined + if (x instanceof B || x instanceof C) { + x; // B | C + } + x; // A | undefined + if (x instanceof B && x instanceof C) { + x; // B & C + } + x; // A | undefined + if (!x) { + return; + } + x; // A + if (x instanceof B) { + x; // B + if (x instanceof C) { + x; // B & C + } + else { + x; // B + } + x; // B + } + else { + x; // A + } + x; // A +} + +// X is neither assignable to Y nor a subtype of Y +// Y is assignable to X, but not a subtype of X + +interface X { + x?: string; +} + +class Y { + y: string; +} + +function goo(x: X) { + x; + if (x instanceof Y) { + x.y; + } + x; +} + +//// [controlFlowInstanceof.js] +// Repros from #10167 +function f1(s) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + s; // Set + s.add(42); +} +function f2(s) { + s = new Set(); + s; // Set + if (s instanceof Promise) { + s; // Set & Promise + } + s; // Set + s.add(42); +} +function f3(s) { + s; // Set | Set + if (s instanceof Set) { + s; // Set | Set + } + else { + s; // never + } +} +function f4(s) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + else { + s; // never + } +} +// More tests +class A { +} +class B extends A { +} +class C extends A { +} +function foo(x) { + x; // A | undefined + if (x instanceof B || x instanceof C) { + x; // B | C + } + x; // A | undefined + if (x instanceof B && x instanceof C) { + x; // B & C + } + x; // A | undefined + if (!x) { + return; + } + x; // A + if (x instanceof B) { + x; // B + if (x instanceof C) { + x; // B & C + } + else { + x; // B + } + x; // B + } + else { + x; // A + } + x; // A +} +class Y { +} +function goo(x) { + x; + if (x instanceof Y) { + x.y; + } + x; +} diff --git a/tests/baselines/reference/controlFlowInstanceof.symbols b/tests/baselines/reference/controlFlowInstanceof.symbols new file mode 100644 index 00000000000..31894827990 --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceof.symbols @@ -0,0 +1,232 @@ +=== tests/cases/compiler/controlFlowInstanceof.ts === + +// Repros from #10167 + +function f1(s: Set | Set) { +>f1 : Symbol(f1, Decl(controlFlowInstanceof.ts, 0, 0)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s = new Set(); +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) + + if (s instanceof Set) { +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) + } + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) + + s.add(42); +>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 3, 12)) +>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +} + +function f2(s: Set | Set) { +>f2 : Symbol(f2, Decl(controlFlowInstanceof.ts, 11, 1)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s = new Set(); +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) + + if (s instanceof Promise) { +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) +>Promise : Symbol(Promise, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --)) + + s; // Set & Promise +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) + } + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) + + s.add(42); +>s.add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 13, 12)) +>add : Symbol(Set.add, Decl(lib.es2015.collection.d.ts, --, --)) +} + +function f3(s: Set | Set) { +>f3 : Symbol(f3, Decl(controlFlowInstanceof.ts, 21, 1)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set | Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) + + if (s instanceof Set) { +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set | Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) + } + else { + s; // never +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 23, 12)) + } +} + +function f4(s: Set | Set) { +>f4 : Symbol(f4, Decl(controlFlowInstanceof.ts, 31, 1)) +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s = new Set(); +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) + + if (s instanceof Set) { +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) +>Set : Symbol(Set, Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --), Decl(lib.es2015.collection.d.ts, --, --)) + + s; // Set +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) + } + else { + s; // never +>s : Symbol(s, Decl(controlFlowInstanceof.ts, 33, 12)) + } +} + +// More tests + +class A { a: string } +>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1)) +>a : Symbol(A.a, Decl(controlFlowInstanceof.ts, 46, 9)) + +class B extends A { b: string } +>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21)) +>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1)) +>b : Symbol(B.b, Decl(controlFlowInstanceof.ts, 47, 19)) + +class C extends A { c: string } +>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31)) +>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1)) +>c : Symbol(C.c, Decl(controlFlowInstanceof.ts, 48, 19)) + +function foo(x: A | undefined) { +>foo : Symbol(foo, Decl(controlFlowInstanceof.ts, 48, 31)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>A : Symbol(A, Decl(controlFlowInstanceof.ts, 42, 1)) + + x; // A | undefined +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (x instanceof B || x instanceof C) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31)) + + x; // B | C +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + x; // A | undefined +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (x instanceof B && x instanceof C) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31)) + + x; // B & C +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + x; // A | undefined +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (!x) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + return; + } + x; // A +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (x instanceof B) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>B : Symbol(B, Decl(controlFlowInstanceof.ts, 46, 21)) + + x; // B +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + + if (x instanceof C) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +>C : Symbol(C, Decl(controlFlowInstanceof.ts, 47, 31)) + + x; // B & C +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + else { + x; // B +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + x; // B +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + else { + x; // A +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) + } + x; // A +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 50, 13)) +} + +// X is neither assignable to Y nor a subtype of Y +// Y is assignable to X, but not a subtype of X + +interface X { +>X : Symbol(X, Decl(controlFlowInstanceof.ts, 78, 1)) + + x?: string; +>x : Symbol(X.x, Decl(controlFlowInstanceof.ts, 83, 13)) +} + +class Y { +>Y : Symbol(Y, Decl(controlFlowInstanceof.ts, 85, 1)) + + y: string; +>y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9)) +} + +function goo(x: X) { +>goo : Symbol(goo, Decl(controlFlowInstanceof.ts, 89, 1)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) +>X : Symbol(X, Decl(controlFlowInstanceof.ts, 78, 1)) + + x; +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) + + if (x instanceof Y) { +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) +>Y : Symbol(Y, Decl(controlFlowInstanceof.ts, 85, 1)) + + x.y; +>x.y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9)) +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) +>y : Symbol(Y.y, Decl(controlFlowInstanceof.ts, 87, 9)) + } + x; +>x : Symbol(x, Decl(controlFlowInstanceof.ts, 91, 13)) +} diff --git a/tests/baselines/reference/controlFlowInstanceof.types b/tests/baselines/reference/controlFlowInstanceof.types new file mode 100644 index 00000000000..e52d1a25b1d --- /dev/null +++ b/tests/baselines/reference/controlFlowInstanceof.types @@ -0,0 +1,256 @@ +=== tests/cases/compiler/controlFlowInstanceof.ts === + +// Repros from #10167 + +function f1(s: Set | Set) { +>f1 : (s: Set | Set) => void +>s : Set | Set +>Set : Set +>Set : Set + + s = new Set(); +>s = new Set() : Set +>s : Set | Set +>new Set() : Set +>Set : SetConstructor + + s; // Set +>s : Set + + if (s instanceof Set) { +>s instanceof Set : boolean +>s : Set +>Set : SetConstructor + + s; // Set +>s : Set + } + s; // Set +>s : Set + + s.add(42); +>s.add(42) : Set +>s.add : (value: number) => Set +>s : Set +>add : (value: number) => Set +>42 : number +} + +function f2(s: Set | Set) { +>f2 : (s: Set | Set) => void +>s : Set | Set +>Set : Set +>Set : Set + + s = new Set(); +>s = new Set() : Set +>s : Set | Set +>new Set() : Set +>Set : SetConstructor + + s; // Set +>s : Set + + if (s instanceof Promise) { +>s instanceof Promise : boolean +>s : Set +>Promise : PromiseConstructor + + s; // Set & Promise +>s : Set & Promise + } + s; // Set +>s : Set + + s.add(42); +>s.add(42) : Set +>s.add : (value: number) => Set +>s : Set +>add : (value: number) => Set +>42 : number +} + +function f3(s: Set | Set) { +>f3 : (s: Set | Set) => void +>s : Set | Set +>Set : Set +>Set : Set + + s; // Set | Set +>s : Set | Set + + if (s instanceof Set) { +>s instanceof Set : boolean +>s : Set | Set +>Set : SetConstructor + + s; // Set | Set +>s : Set | Set + } + else { + s; // never +>s : never + } +} + +function f4(s: Set | Set) { +>f4 : (s: Set | Set) => void +>s : Set | Set +>Set : Set +>Set : Set + + s = new Set(); +>s = new Set() : Set +>s : Set | Set +>new Set() : Set +>Set : SetConstructor + + s; // Set +>s : Set + + if (s instanceof Set) { +>s instanceof Set : boolean +>s : Set +>Set : SetConstructor + + s; // Set +>s : Set + } + else { + s; // never +>s : never + } +} + +// More tests + +class A { a: string } +>A : A +>a : string + +class B extends A { b: string } +>B : B +>A : A +>b : string + +class C extends A { c: string } +>C : C +>A : A +>c : string + +function foo(x: A | undefined) { +>foo : (x: A) => void +>x : A +>A : A + + x; // A | undefined +>x : A + + if (x instanceof B || x instanceof C) { +>x instanceof B || x instanceof C : boolean +>x instanceof B : boolean +>x : A +>B : typeof B +>x instanceof C : boolean +>x : A +>C : typeof C + + x; // B | C +>x : B | C + } + x; // A | undefined +>x : A + + if (x instanceof B && x instanceof C) { +>x instanceof B && x instanceof C : boolean +>x instanceof B : boolean +>x : A +>B : typeof B +>x instanceof C : boolean +>x : B +>C : typeof C + + x; // B & C +>x : B & C + } + x; // A | undefined +>x : A + + if (!x) { +>!x : boolean +>x : A + + return; + } + x; // A +>x : A + + if (x instanceof B) { +>x instanceof B : boolean +>x : A +>B : typeof B + + x; // B +>x : B + + if (x instanceof C) { +>x instanceof C : boolean +>x : B +>C : typeof C + + x; // B & C +>x : B & C + } + else { + x; // B +>x : B + } + x; // B +>x : B + } + else { + x; // A +>x : A + } + x; // A +>x : A +} + +// X is neither assignable to Y nor a subtype of Y +// Y is assignable to X, but not a subtype of X + +interface X { +>X : X + + x?: string; +>x : string +} + +class Y { +>Y : Y + + y: string; +>y : string +} + +function goo(x: X) { +>goo : (x: X) => void +>x : X +>X : X + + x; +>x : X + + if (x instanceof Y) { +>x instanceof Y : boolean +>x : X +>Y : typeof Y + + x.y; +>x.y : string +>x : Y +>y : string + } + x; +>x : X +} diff --git a/tests/baselines/reference/discriminantPropertyCheck.errors.txt b/tests/baselines/reference/discriminantPropertyCheck.errors.txt new file mode 100644 index 00000000000..41593c4b224 --- /dev/null +++ b/tests/baselines/reference/discriminantPropertyCheck.errors.txt @@ -0,0 +1,77 @@ +tests/cases/compiler/discriminantPropertyCheck.ts(30,9): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/discriminantPropertyCheck.ts(66,9): error TS2532: Object is possibly 'undefined'. + + +==== tests/cases/compiler/discriminantPropertyCheck.ts (2 errors) ==== + + type Item = Item1 | Item2; + + interface Base { + bar: boolean; + } + + interface Item1 extends Base { + kind: "A"; + foo: string | undefined; + baz: boolean; + qux: true; + } + + interface Item2 extends Base { + kind: "B"; + foo: string | undefined; + baz: boolean; + qux: false; + } + + function goo1(x: Item) { + if (x.kind === "A" && x.foo !== undefined) { + x.foo.length; + } + } + + function goo2(x: Item) { + if (x.foo !== undefined && x.kind === "A") { + x.foo.length; // Error, intervening discriminant guard + ~~~~~ +!!! error TS2532: Object is possibly 'undefined'. + } + } + + function foo1(x: Item) { + if (x.bar && x.foo !== undefined) { + x.foo.length; + } + } + + function foo2(x: Item) { + if (x.foo !== undefined && x.bar) { + x.foo.length; + } + } + + function foo3(x: Item) { + if (x.baz && x.foo !== undefined) { + x.foo.length; + } + } + + function foo4(x: Item) { + if (x.foo !== undefined && x.baz) { + x.foo.length; + } + } + + function foo5(x: Item) { + if (x.qux && x.foo !== undefined) { + x.foo.length; + } + } + + function foo6(x: Item) { + if (x.foo !== undefined && x.qux) { + x.foo.length; // Error, intervening discriminant guard + ~~~~~ +!!! error TS2532: Object is possibly 'undefined'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/discriminantPropertyCheck.js b/tests/baselines/reference/discriminantPropertyCheck.js new file mode 100644 index 00000000000..b75a6277735 --- /dev/null +++ b/tests/baselines/reference/discriminantPropertyCheck.js @@ -0,0 +1,111 @@ +//// [discriminantPropertyCheck.ts] + +type Item = Item1 | Item2; + +interface Base { + bar: boolean; +} + +interface Item1 extends Base { + kind: "A"; + foo: string | undefined; + baz: boolean; + qux: true; +} + +interface Item2 extends Base { + kind: "B"; + foo: string | undefined; + baz: boolean; + qux: false; +} + +function goo1(x: Item) { + if (x.kind === "A" && x.foo !== undefined) { + x.foo.length; + } +} + +function goo2(x: Item) { + if (x.foo !== undefined && x.kind === "A") { + x.foo.length; // Error, intervening discriminant guard + } +} + +function foo1(x: Item) { + if (x.bar && x.foo !== undefined) { + x.foo.length; + } +} + +function foo2(x: Item) { + if (x.foo !== undefined && x.bar) { + x.foo.length; + } +} + +function foo3(x: Item) { + if (x.baz && x.foo !== undefined) { + x.foo.length; + } +} + +function foo4(x: Item) { + if (x.foo !== undefined && x.baz) { + x.foo.length; + } +} + +function foo5(x: Item) { + if (x.qux && x.foo !== undefined) { + x.foo.length; + } +} + +function foo6(x: Item) { + if (x.foo !== undefined && x.qux) { + x.foo.length; // Error, intervening discriminant guard + } +} + +//// [discriminantPropertyCheck.js] +function goo1(x) { + if (x.kind === "A" && x.foo !== undefined) { + x.foo.length; + } +} +function goo2(x) { + if (x.foo !== undefined && x.kind === "A") { + x.foo.length; // Error, intervening discriminant guard + } +} +function foo1(x) { + if (x.bar && x.foo !== undefined) { + x.foo.length; + } +} +function foo2(x) { + if (x.foo !== undefined && x.bar) { + x.foo.length; + } +} +function foo3(x) { + if (x.baz && x.foo !== undefined) { + x.foo.length; + } +} +function foo4(x) { + if (x.foo !== undefined && x.baz) { + x.foo.length; + } +} +function foo5(x) { + if (x.qux && x.foo !== undefined) { + x.foo.length; + } +} +function foo6(x) { + if (x.foo !== undefined && x.qux) { + x.foo.length; // Error, intervening discriminant guard + } +} diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.js b/tests/baselines/reference/discriminantsAndNullOrUndefined.js new file mode 100644 index 00000000000..153950f8ab5 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.js @@ -0,0 +1,44 @@ +//// [discriminantsAndNullOrUndefined.ts] + +// Repro from #10228 + +interface A { kind: 'A'; } +interface B { kind: 'B'; } + +type C = A | B | undefined; + +function never(_: never): never { + throw new Error(); +} + +function useA(_: A): void { } +function useB(_: B): void { } + +declare var c: C; + +if (c !== undefined) { + switch (c.kind) { + case 'A': useA(c); break; + case 'B': useB(c); break; + default: never(c); + } +} + +//// [discriminantsAndNullOrUndefined.js] +// Repro from #10228 +function never(_) { + throw new Error(); +} +function useA(_) { } +function useB(_) { } +if (c !== undefined) { + switch (c.kind) { + case 'A': + useA(c); + break; + case 'B': + useB(c); + break; + default: never(c); + } +} diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols b/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols new file mode 100644 index 00000000000..3f95fcf3a3f --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.symbols @@ -0,0 +1,61 @@ +=== tests/cases/compiler/discriminantsAndNullOrUndefined.ts === + +// Repro from #10228 + +interface A { kind: 'A'; } +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) +>kind : Symbol(A.kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13)) + +interface B { kind: 'B'; } +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) +>kind : Symbol(B.kind, Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) + +type C = A | B | undefined; +>C : Symbol(C, Decl(discriminantsAndNullOrUndefined.ts, 4, 26)) +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) + +function never(_: never): never { +>never : Symbol(never, Decl(discriminantsAndNullOrUndefined.ts, 6, 27)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 8, 15)) + + throw new Error(); +>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) +} + +function useA(_: A): void { } +>useA : Symbol(useA, Decl(discriminantsAndNullOrUndefined.ts, 10, 1)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 12, 14)) +>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0)) + +function useB(_: B): void { } +>useB : Symbol(useB, Decl(discriminantsAndNullOrUndefined.ts, 12, 29)) +>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 13, 14)) +>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26)) + +declare var c: C; +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>C : Symbol(C, Decl(discriminantsAndNullOrUndefined.ts, 4, 26)) + +if (c !== undefined) { +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>undefined : Symbol(undefined) + + switch (c.kind) { +>c.kind : Symbol(kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13), Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) +>kind : Symbol(kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13), Decl(discriminantsAndNullOrUndefined.ts, 4, 13)) + + case 'A': useA(c); break; +>useA : Symbol(useA, Decl(discriminantsAndNullOrUndefined.ts, 10, 1)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + + case 'B': useB(c); break; +>useB : Symbol(useB, Decl(discriminantsAndNullOrUndefined.ts, 12, 29)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + + default: never(c); +>never : Symbol(never, Decl(discriminantsAndNullOrUndefined.ts, 6, 27)) +>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11)) + } +} diff --git a/tests/baselines/reference/discriminantsAndNullOrUndefined.types b/tests/baselines/reference/discriminantsAndNullOrUndefined.types new file mode 100644 index 00000000000..7a2918ab83b --- /dev/null +++ b/tests/baselines/reference/discriminantsAndNullOrUndefined.types @@ -0,0 +1,68 @@ +=== tests/cases/compiler/discriminantsAndNullOrUndefined.ts === + +// Repro from #10228 + +interface A { kind: 'A'; } +>A : A +>kind : "A" + +interface B { kind: 'B'; } +>B : B +>kind : "B" + +type C = A | B | undefined; +>C : C +>A : A +>B : B + +function never(_: never): never { +>never : (_: never) => never +>_ : never + + throw new Error(); +>new Error() : Error +>Error : ErrorConstructor +} + +function useA(_: A): void { } +>useA : (_: A) => void +>_ : A +>A : A + +function useB(_: B): void { } +>useB : (_: B) => void +>_ : B +>B : B + +declare var c: C; +>c : C +>C : C + +if (c !== undefined) { +>c !== undefined : boolean +>c : C +>undefined : undefined + + switch (c.kind) { +>c.kind : "A" | "B" +>c : A | B +>kind : "A" | "B" + + case 'A': useA(c); break; +>'A' : "A" +>useA(c) : void +>useA : (_: A) => void +>c : A + + case 'B': useB(c); break; +>'B' : "B" +>useB(c) : void +>useB : (_: B) => void +>c : B + + default: never(c); +>never(c) : never +>never : (_: never) => never +>c : never + } +} diff --git a/tests/baselines/reference/discriminantsAndPrimitives.js b/tests/baselines/reference/discriminantsAndPrimitives.js new file mode 100644 index 00000000000..1d11781a707 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndPrimitives.js @@ -0,0 +1,84 @@ +//// [discriminantsAndPrimitives.ts] + +// Repro from #10257 plus other tests + +interface Foo { + kind: "foo"; + name: string; +} + +interface Bar { + kind: "bar"; + length: string; +} + +function f1(x: Foo | Bar | string) { + if (typeof x !== 'string') { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f2(x: Foo | Bar | string | undefined) { + if (typeof x === "object") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f3(x: Foo | Bar | string | null) { + if (x && typeof x !== "string") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f4(x: Foo | Bar | string | number | null) { + if (x && typeof x === "object") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +//// [discriminantsAndPrimitives.js] +// Repro from #10257 plus other tests +function f1(x) { + if (typeof x !== 'string') { + switch (x.kind) { + case 'foo': + x.name; + } + } +} +function f2(x) { + if (typeof x === "object") { + switch (x.kind) { + case 'foo': + x.name; + } + } +} +function f3(x) { + if (x && typeof x !== "string") { + switch (x.kind) { + case 'foo': + x.name; + } + } +} +function f4(x) { + if (x && typeof x === "object") { + switch (x.kind) { + case 'foo': + x.name; + } + } +} diff --git a/tests/baselines/reference/discriminantsAndPrimitives.symbols b/tests/baselines/reference/discriminantsAndPrimitives.symbols new file mode 100644 index 00000000000..c84f32cd990 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndPrimitives.symbols @@ -0,0 +1,117 @@ +=== tests/cases/compiler/discriminantsAndPrimitives.ts === + +// Repro from #10257 plus other tests + +interface Foo { +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) + + kind: "foo"; +>kind : Symbol(Foo.kind, Decl(discriminantsAndPrimitives.ts, 3, 15)) + + name: string; +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +} + +interface Bar { +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + kind: "bar"; +>kind : Symbol(Bar.kind, Decl(discriminantsAndPrimitives.ts, 8, 15)) + + length: string; +>length : Symbol(Bar.length, Decl(discriminantsAndPrimitives.ts, 9, 16)) +} + +function f1(x: Foo | Bar | string) { +>f1 : Symbol(f1, Decl(discriminantsAndPrimitives.ts, 11, 1)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 13, 12)) +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + if (typeof x !== 'string') { +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 13, 12)) + + switch(x.kind) { +>x.kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 13, 12)) +>kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) + + case 'foo': + x.name; +>x.name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 13, 12)) +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) + } + } +} + +function f2(x: Foo | Bar | string | undefined) { +>f2 : Symbol(f2, Decl(discriminantsAndPrimitives.ts, 20, 1)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 22, 12)) +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + if (typeof x === "object") { +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 22, 12)) + + switch(x.kind) { +>x.kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 22, 12)) +>kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) + + case 'foo': + x.name; +>x.name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 22, 12)) +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) + } + } +} + +function f3(x: Foo | Bar | string | null) { +>f3 : Symbol(f3, Decl(discriminantsAndPrimitives.ts, 29, 1)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + if (x && typeof x !== "string") { +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) + + switch(x.kind) { +>x.kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) +>kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) + + case 'foo': + x.name; +>x.name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 31, 12)) +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) + } + } +} + +function f4(x: Foo | Bar | string | number | null) { +>f4 : Symbol(f4, Decl(discriminantsAndPrimitives.ts, 38, 1)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) +>Foo : Symbol(Foo, Decl(discriminantsAndPrimitives.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(discriminantsAndPrimitives.ts, 6, 1)) + + if (x && typeof x === "object") { +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) + + switch(x.kind) { +>x.kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) +>kind : Symbol(kind, Decl(discriminantsAndPrimitives.ts, 3, 15), Decl(discriminantsAndPrimitives.ts, 8, 15)) + + case 'foo': + x.name; +>x.name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) +>x : Symbol(x, Decl(discriminantsAndPrimitives.ts, 40, 12)) +>name : Symbol(Foo.name, Decl(discriminantsAndPrimitives.ts, 4, 16)) + } + } +} diff --git a/tests/baselines/reference/discriminantsAndPrimitives.types b/tests/baselines/reference/discriminantsAndPrimitives.types new file mode 100644 index 00000000000..d3e1b70e599 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndPrimitives.types @@ -0,0 +1,141 @@ +=== tests/cases/compiler/discriminantsAndPrimitives.ts === + +// Repro from #10257 plus other tests + +interface Foo { +>Foo : Foo + + kind: "foo"; +>kind : "foo" + + name: string; +>name : string +} + +interface Bar { +>Bar : Bar + + kind: "bar"; +>kind : "bar" + + length: string; +>length : string +} + +function f1(x: Foo | Bar | string) { +>f1 : (x: string | Foo | Bar) => void +>x : string | Foo | Bar +>Foo : Foo +>Bar : Bar + + if (typeof x !== 'string') { +>typeof x !== 'string' : boolean +>typeof x : string +>x : string | Foo | Bar +>'string' : "string" + + switch(x.kind) { +>x.kind : "foo" | "bar" +>x : Foo | Bar +>kind : "foo" | "bar" + + case 'foo': +>'foo' : "foo" + + x.name; +>x.name : string +>x : Foo +>name : string + } + } +} + +function f2(x: Foo | Bar | string | undefined) { +>f2 : (x: string | Foo | Bar | undefined) => void +>x : string | Foo | Bar | undefined +>Foo : Foo +>Bar : Bar + + if (typeof x === "object") { +>typeof x === "object" : boolean +>typeof x : string +>x : string | Foo | Bar | undefined +>"object" : "object" + + switch(x.kind) { +>x.kind : "foo" | "bar" +>x : Foo | Bar +>kind : "foo" | "bar" + + case 'foo': +>'foo' : "foo" + + x.name; +>x.name : string +>x : Foo +>name : string + } + } +} + +function f3(x: Foo | Bar | string | null) { +>f3 : (x: string | Foo | Bar | null) => void +>x : string | Foo | Bar | null +>Foo : Foo +>Bar : Bar +>null : null + + if (x && typeof x !== "string") { +>x && typeof x !== "string" : boolean | "" | null +>x : string | Foo | Bar | null +>typeof x !== "string" : boolean +>typeof x : string +>x : string | Foo | Bar +>"string" : "string" + + switch(x.kind) { +>x.kind : "foo" | "bar" +>x : Foo | Bar +>kind : "foo" | "bar" + + case 'foo': +>'foo' : "foo" + + x.name; +>x.name : string +>x : Foo +>name : string + } + } +} + +function f4(x: Foo | Bar | string | number | null) { +>f4 : (x: string | number | Foo | Bar | null) => void +>x : string | number | Foo | Bar | null +>Foo : Foo +>Bar : Bar +>null : null + + if (x && typeof x === "object") { +>x && typeof x === "object" : boolean | "" | 0 | null +>x : string | number | Foo | Bar | null +>typeof x === "object" : boolean +>typeof x : string +>x : string | number | Foo | Bar +>"object" : "object" + + switch(x.kind) { +>x.kind : "foo" | "bar" +>x : Foo | Bar +>kind : "foo" | "bar" + + case 'foo': +>'foo' : "foo" + + x.name; +>x.name : string +>x : Foo +>name : string + } + } +} diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.js b/tests/baselines/reference/discriminantsAndTypePredicates.js new file mode 100644 index 00000000000..66cfe056ab7 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndTypePredicates.js @@ -0,0 +1,59 @@ +//// [discriminantsAndTypePredicates.ts] +// Repro from #10145 + +interface A { type: 'A' } +interface B { type: 'B' } + +function isA(x: A | B): x is A { return x.type === 'A'; } +function isB(x: A | B): x is B { return x.type === 'B'; } + +function foo1(x: A | B): any { + x; // A | B + if (isA(x)) { + return x; // A + } + x; // B + if (isB(x)) { + return x; // B + } + x; // never +} + +function foo2(x: A | B): any { + x; // A | B + if (x.type === 'A') { + return x; // A + } + x; // B + if (x.type === 'B') { + return x; // B + } + x; // never +} + +//// [discriminantsAndTypePredicates.js] +// Repro from #10145 +function isA(x) { return x.type === 'A'; } +function isB(x) { return x.type === 'B'; } +function foo1(x) { + x; // A | B + if (isA(x)) { + return x; // A + } + x; // B + if (isB(x)) { + return x; // B + } + x; // never +} +function foo2(x) { + x; // A | B + if (x.type === 'A') { + return x; // A + } + x; // B + if (x.type === 'B') { + return x; // B + } + x; // never +} diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.symbols b/tests/baselines/reference/discriminantsAndTypePredicates.symbols new file mode 100644 index 00000000000..4f412823936 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndTypePredicates.symbols @@ -0,0 +1,94 @@ +=== tests/cases/compiler/discriminantsAndTypePredicates.ts === +// Repro from #10145 + +interface A { type: 'A' } +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>type : Symbol(A.type, Decl(discriminantsAndTypePredicates.ts, 2, 13)) + +interface B { type: 'B' } +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) +>type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13)) + +function isA(x: A | B): x is A { return x.type === 'A'; } +>isA : Symbol(isA, Decl(discriminantsAndTypePredicates.ts, 3, 25)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 5, 13)) +>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) + +function isB(x: A | B): x is B { return x.type === 'B'; } +>isB : Symbol(isB, Decl(discriminantsAndTypePredicates.ts, 5, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) +>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 6, 13)) +>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) + +function foo1(x: A | B): any { +>foo1 : Symbol(foo1, Decl(discriminantsAndTypePredicates.ts, 6, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) + + x; // A | B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + + if (isA(x)) { +>isA : Symbol(isA, Decl(discriminantsAndTypePredicates.ts, 3, 25)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + + return x; // A +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + } + x; // B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + + if (isB(x)) { +>isB : Symbol(isB, Decl(discriminantsAndTypePredicates.ts, 5, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + + return x; // B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) + } + x; // never +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 8, 14)) +} + +function foo2(x: A | B): any { +>foo2 : Symbol(foo2, Decl(discriminantsAndTypePredicates.ts, 18, 1)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) +>A : Symbol(A, Decl(discriminantsAndTypePredicates.ts, 0, 0)) +>B : Symbol(B, Decl(discriminantsAndTypePredicates.ts, 2, 25)) + + x; // A | B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) + + if (x.type === 'A') { +>x.type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) +>type : Symbol(type, Decl(discriminantsAndTypePredicates.ts, 2, 13), Decl(discriminantsAndTypePredicates.ts, 3, 13)) + + return x; // A +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) + } + x; // B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) + + if (x.type === 'B') { +>x.type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) +>type : Symbol(B.type, Decl(discriminantsAndTypePredicates.ts, 3, 13)) + + return x; // B +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) + } + x; // never +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) +} diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.types b/tests/baselines/reference/discriminantsAndTypePredicates.types new file mode 100644 index 00000000000..f7675b14756 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndTypePredicates.types @@ -0,0 +1,104 @@ +=== tests/cases/compiler/discriminantsAndTypePredicates.ts === +// Repro from #10145 + +interface A { type: 'A' } +>A : A +>type : "A" + +interface B { type: 'B' } +>B : B +>type : "B" + +function isA(x: A | B): x is A { return x.type === 'A'; } +>isA : (x: A | B) => x is A +>x : A | B +>A : A +>B : B +>x : any +>A : A +>x.type === 'A' : boolean +>x.type : "A" | "B" +>x : A | B +>type : "A" | "B" +>'A' : "A" + +function isB(x: A | B): x is B { return x.type === 'B'; } +>isB : (x: A | B) => x is B +>x : A | B +>A : A +>B : B +>x : any +>B : B +>x.type === 'B' : boolean +>x.type : "A" | "B" +>x : A | B +>type : "A" | "B" +>'B' : "B" + +function foo1(x: A | B): any { +>foo1 : (x: A | B) => any +>x : A | B +>A : A +>B : B + + x; // A | B +>x : A | B + + if (isA(x)) { +>isA(x) : boolean +>isA : (x: A | B) => x is A +>x : A | B + + return x; // A +>x : A + } + x; // B +>x : B + + if (isB(x)) { +>isB(x) : boolean +>isB : (x: A | B) => x is B +>x : B + + return x; // B +>x : B + } + x; // never +>x : never +} + +function foo2(x: A | B): any { +>foo2 : (x: A | B) => any +>x : A | B +>A : A +>B : B + + x; // A | B +>x : A | B + + if (x.type === 'A') { +>x.type === 'A' : boolean +>x.type : "A" | "B" +>x : A | B +>type : "A" | "B" +>'A' : "A" + + return x; // A +>x : A + } + x; // B +>x : B + + if (x.type === 'B') { +>x.type === 'B' : boolean +>x.type : "B" +>x : B +>type : "B" +>'B' : "B" + + return x; // B +>x : B + } + x; // never +>x : never +} diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt new file mode 100644 index 00000000000..d0f11d056ca --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.errors.txt @@ -0,0 +1,13 @@ +tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts(3,17): error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. +tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts(3,29): error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + + +==== tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts (2 errors) ==== + declare function wrapper(x: any); + wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[any]' with length '1' cannot be assigned to tuple with length '3'. + }); \ No newline at end of file diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js new file mode 100644 index 00000000000..9dd7453ba4f --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring1.js @@ -0,0 +1,11 @@ +//// [emitCapturingThisInTupleDestructuring1.ts] +declare function wrapper(x: any); +wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" +}); + +//// [emitCapturingThisInTupleDestructuring1.js] +var _this = this; +wrapper(function (array) { + _this.test = array[0], _this.test1 = array[1], _this.test2 = array[2]; // even though there is a compiler error, we should still emit lexical capture for "this" +}); diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt new file mode 100644 index 00000000000..25207e28c79 --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.errors.txt @@ -0,0 +1,16 @@ +tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts(8,39): error TS2493: Tuple type '[number, number]' with length '2' cannot be assigned to tuple with length '3'. + + +==== tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts (1 errors) ==== + var array1: [number, number] = [1, 2]; + + class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + ~~~~~~~~~~ +!!! error TS2493: Tuple type '[number, number]' with length '2' cannot be assigned to tuple with length '3'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js new file mode 100644 index 00000000000..f999cfe70f0 --- /dev/null +++ b/tests/baselines/reference/emitCapturingThisInTupleDestructuring2.js @@ -0,0 +1,23 @@ +//// [emitCapturingThisInTupleDestructuring2.ts] +var array1: [number, number] = [1, 2]; + +class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + } +} + +//// [emitCapturingThisInTupleDestructuring2.js] +var array1 = [1, 2]; +var B = (function () { + function B() { + } + B.prototype.method = function () { + var _this = this; + (function () { return (_this.test = array1[0], _this.test1 = array1[1], _this.test2 = array1[2], array1); }); // even though there is a compiler error, we should still emit lexical capture for "this" + }; + return B; +}()); diff --git a/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js b/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js index 03fb45b806e..91e6c506c2b 100644 --- a/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js +++ b/tests/baselines/reference/emitClassDeclarationWithPropertyAssignmentInES6.js @@ -37,8 +37,8 @@ class D { } } class E extends D { - constructor(...args) { - super(...args); + constructor() { + super(...arguments); this.z = true; } } diff --git a/tests/baselines/reference/exportDefaultProperty.js b/tests/baselines/reference/exportDefaultProperty.js new file mode 100644 index 00000000000..10ba6d6419a --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty.js @@ -0,0 +1,76 @@ +//// [tests/cases/compiler/exportDefaultProperty.ts] //// + +//// [declarations.d.ts] +// This test is just like exportEqualsProperty, but with `export default`. + +declare namespace foo.bar { + export type X = number; + export const X: number; +} + +declare module "foobar" { + export default foo.bar; +} + +declare module "foobarx" { + export default foo.bar.X; +} + +//// [a.ts] +namespace A { + export class B { constructor(b: number) {} } + export namespace B { export const b: number = 0; } +} +export default A.B; + +//// [b.ts] +export default "foo".length; + +//// [index.ts] +/// +import fooBar from "foobar"; +import X = fooBar.X; +import X2 from "foobarx"; +const x: X = X; +const x2: X2 = X2; + +import B from "./a"; +const b: B = new B(B.b); + +import fooLength from "./b"; +fooLength + 1; + + +//// [a.js] +"use strict"; +var A; +(function (A) { + var B = (function () { + function B(b) { + } + return B; + }()); + A.B = B; + var B; + (function (B) { + B.b = 0; + })(B = A.B || (A.B = {})); +})(A || (A = {})); +exports.__esModule = true; +exports["default"] = A.B; +//// [b.js] +"use strict"; +exports.__esModule = true; +exports["default"] = "foo".length; +//// [index.js] +"use strict"; +/// +var foobar_1 = require("foobar"); +var X = foobar_1["default"].X; +var foobarx_1 = require("foobarx"); +var x = X; +var x2 = foobarx_1["default"]; +var a_1 = require("./a"); +var b = new a_1["default"](a_1["default"].b); +var b_1 = require("./b"); +b_1["default"] + 1; diff --git a/tests/baselines/reference/exportDefaultProperty.symbols b/tests/baselines/reference/exportDefaultProperty.symbols new file mode 100644 index 00000000000..f9edcd154cc --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty.symbols @@ -0,0 +1,92 @@ +=== tests/cases/compiler/index.ts === +/// +import fooBar from "foobar"; +>fooBar : Symbol(fooBar, Decl(index.ts, 1, 6)) + +import X = fooBar.X; +>X : Symbol(X, Decl(index.ts, 1, 28)) +>fooBar : Symbol(fooBar, Decl(index.ts, 1, 6)) +>X : Symbol(fooBar.X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) + +import X2 from "foobarx"; +>X2 : Symbol(X2, Decl(index.ts, 3, 6)) + +const x: X = X; +>x : Symbol(x, Decl(index.ts, 4, 5)) +>X : Symbol(X, Decl(index.ts, 1, 28)) +>X : Symbol(X, Decl(index.ts, 1, 28)) + +const x2: X2 = X2; +>x2 : Symbol(x2, Decl(index.ts, 5, 5)) +>X2 : Symbol(X2, Decl(index.ts, 3, 6)) +>X2 : Symbol(X2, Decl(index.ts, 3, 6)) + +import B from "./a"; +>B : Symbol(B, Decl(index.ts, 7, 6)) + +const b: B = new B(B.b); +>b : Symbol(b, Decl(index.ts, 8, 5)) +>B : Symbol(B, Decl(index.ts, 7, 6)) +>B : Symbol(B, Decl(index.ts, 7, 6)) +>B.b : Symbol(B.b, Decl(a.ts, 2, 37)) +>B : Symbol(B, Decl(index.ts, 7, 6)) +>b : Symbol(B.b, Decl(a.ts, 2, 37)) + +import fooLength from "./b"; +>fooLength : Symbol(fooLength, Decl(index.ts, 10, 6)) + +fooLength + 1; +>fooLength : Symbol(fooLength, Decl(index.ts, 10, 6)) + +=== tests/cases/compiler/declarations.d.ts === +// This test is just like exportEqualsProperty, but with `export default`. + +declare namespace foo.bar { +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(bar, Decl(declarations.d.ts, 2, 22)) + + export type X = number; +>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) + + export const X: number; +>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +} + +declare module "foobar" { + export default foo.bar; +>foo.bar : Symbol(default, Decl(declarations.d.ts, 2, 22)) +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(default, Decl(declarations.d.ts, 2, 22)) +} + +declare module "foobarx" { + export default foo.bar.X; +>foo.bar.X : Symbol(default, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +>foo.bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>X : Symbol(default, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +} + +=== tests/cases/compiler/a.ts === +namespace A { +>A : Symbol(A, Decl(a.ts, 0, 0)) + + export class B { constructor(b: number) {} } +>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>b : Symbol(b, Decl(a.ts, 1, 33)) + + export namespace B { export const b: number = 0; } +>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>b : Symbol(b, Decl(a.ts, 2, 37)) +} +export default A.B; +>A.B : Symbol(default, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>A : Symbol(A, Decl(a.ts, 0, 0)) +>B : Symbol(default, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) + +=== tests/cases/compiler/b.ts === +export default "foo".length; +>"foo".length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/exportDefaultProperty.types b/tests/baselines/reference/exportDefaultProperty.types new file mode 100644 index 00000000000..47cfabfbc16 --- /dev/null +++ b/tests/baselines/reference/exportDefaultProperty.types @@ -0,0 +1,97 @@ +=== tests/cases/compiler/index.ts === +/// +import fooBar from "foobar"; +>fooBar : typeof fooBar + +import X = fooBar.X; +>X : number +>fooBar : typeof fooBar +>X : number + +import X2 from "foobarx"; +>X2 : number + +const x: X = X; +>x : number +>X : number +>X : number + +const x2: X2 = X2; +>x2 : number +>X2 : number +>X2 : number + +import B from "./a"; +>B : typeof B + +const b: B = new B(B.b); +>b : B +>B : B +>new B(B.b) : B +>B : typeof B +>B.b : number +>B : typeof B +>b : number + +import fooLength from "./b"; +>fooLength : number + +fooLength + 1; +>fooLength + 1 : number +>fooLength : number +>1 : number + +=== tests/cases/compiler/declarations.d.ts === +// This test is just like exportEqualsProperty, but with `export default`. + +declare namespace foo.bar { +>foo : typeof foo +>bar : typeof bar + + export type X = number; +>X : number + + export const X: number; +>X : number +} + +declare module "foobar" { + export default foo.bar; +>foo.bar : typeof default +>foo : typeof foo +>bar : typeof default +} + +declare module "foobarx" { + export default foo.bar.X; +>foo.bar.X : number +>foo.bar : typeof foo.bar +>foo : typeof foo +>bar : typeof foo.bar +>X : number +} + +=== tests/cases/compiler/a.ts === +namespace A { +>A : typeof A + + export class B { constructor(b: number) {} } +>B : B +>b : number + + export namespace B { export const b: number = 0; } +>B : typeof B +>b : number +>0 : number +} +export default A.B; +>A.B : typeof default +>A : typeof A +>B : typeof default + +=== tests/cases/compiler/b.ts === +export default "foo".length; +>"foo".length : number +>"foo" : string +>length : number + diff --git a/tests/baselines/reference/exportEqualsProperty.js b/tests/baselines/reference/exportEqualsProperty.js new file mode 100644 index 00000000000..2fd8a8c8511 --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty.js @@ -0,0 +1,72 @@ +//// [tests/cases/compiler/exportEqualsProperty.ts] //// + +//// [declarations.d.ts] +// This test is just like exportDefaultProperty, but with `export =`. + +declare namespace foo.bar { + export type X = number; + export const X: number; +} + +declare module "foobar" { + export = foo.bar; +} + +declare module "foobarx" { + export = foo.bar.X; +} + +//// [a.ts] +namespace A { + export class B { constructor(b: number) {} } + export namespace B { export const b: number = 0; } +} +export = A.B; + +//// [b.ts] +export = "foo".length; + +//// [index.ts] +/// +import { X } from "foobar"; +import X2 = require("foobarx"); +const x: X = X; +const x2: X2 = X2; + +import B = require("./a"); +const b: B = new B(B.b); + +import fooLength = require("./b"); +fooLength + 1; + + +//// [a.js] +"use strict"; +var A; +(function (A) { + var B = (function () { + function B(b) { + } + return B; + }()); + A.B = B; + var B; + (function (B) { + B.b = 0; + })(B = A.B || (A.B = {})); +})(A || (A = {})); +module.exports = A.B; +//// [b.js] +"use strict"; +module.exports = "foo".length; +//// [index.js] +"use strict"; +/// +var foobar_1 = require("foobar"); +var X2 = require("foobarx"); +var x = foobar_1.X; +var x2 = X2; +var B = require("./a"); +var b = new B(B.b); +var fooLength = require("./b"); +fooLength + 1; diff --git a/tests/baselines/reference/exportEqualsProperty.symbols b/tests/baselines/reference/exportEqualsProperty.symbols new file mode 100644 index 00000000000..43c9ed32518 --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty.symbols @@ -0,0 +1,87 @@ +=== tests/cases/compiler/index.ts === +/// +import { X } from "foobar"; +>X : Symbol(X, Decl(index.ts, 1, 8)) + +import X2 = require("foobarx"); +>X2 : Symbol(X2, Decl(index.ts, 1, 27)) + +const x: X = X; +>x : Symbol(x, Decl(index.ts, 3, 5)) +>X : Symbol(X, Decl(index.ts, 1, 8)) +>X : Symbol(X, Decl(index.ts, 1, 8)) + +const x2: X2 = X2; +>x2 : Symbol(x2, Decl(index.ts, 4, 5)) +>X2 : Symbol(X2, Decl(index.ts, 1, 27)) +>X2 : Symbol(X2, Decl(index.ts, 1, 27)) + +import B = require("./a"); +>B : Symbol(B, Decl(index.ts, 4, 18)) + +const b: B = new B(B.b); +>b : Symbol(b, Decl(index.ts, 7, 5)) +>B : Symbol(B, Decl(index.ts, 4, 18)) +>B : Symbol(B, Decl(index.ts, 4, 18)) +>B.b : Symbol(B.b, Decl(a.ts, 2, 37)) +>B : Symbol(B, Decl(index.ts, 4, 18)) +>b : Symbol(B.b, Decl(a.ts, 2, 37)) + +import fooLength = require("./b"); +>fooLength : Symbol(fooLength, Decl(index.ts, 7, 24)) + +fooLength + 1; +>fooLength : Symbol(fooLength, Decl(index.ts, 7, 24)) + +=== tests/cases/compiler/declarations.d.ts === +// This test is just like exportDefaultProperty, but with `export =`. + +declare namespace foo.bar { +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(bar, Decl(declarations.d.ts, 2, 22)) + + export type X = number; +>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) + + export const X: number; +>X : Symbol(X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +} + +declare module "foobar" { + export = foo.bar; +>foo.bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +} + +declare module "foobarx" { + export = foo.bar.X; +>foo.bar.X : Symbol(foo.bar.X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +>foo.bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 0)) +>bar : Symbol(foo.bar, Decl(declarations.d.ts, 2, 22)) +>X : Symbol(foo.bar.X, Decl(declarations.d.ts, 2, 27), Decl(declarations.d.ts, 4, 16)) +} + +=== tests/cases/compiler/a.ts === +namespace A { +>A : Symbol(A, Decl(a.ts, 0, 0)) + + export class B { constructor(b: number) {} } +>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>b : Symbol(b, Decl(a.ts, 1, 33)) + + export namespace B { export const b: number = 0; } +>B : Symbol(B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>b : Symbol(b, Decl(a.ts, 2, 37)) +} +export = A.B; +>A.B : Symbol(A.B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) +>A : Symbol(A, Decl(a.ts, 0, 0)) +>B : Symbol(A.B, Decl(a.ts, 0, 13), Decl(a.ts, 1, 48)) + +=== tests/cases/compiler/b.ts === +export = "foo".length; +>"foo".length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/exportEqualsProperty.types b/tests/baselines/reference/exportEqualsProperty.types new file mode 100644 index 00000000000..a92af53b12b --- /dev/null +++ b/tests/baselines/reference/exportEqualsProperty.types @@ -0,0 +1,92 @@ +=== tests/cases/compiler/index.ts === +/// +import { X } from "foobar"; +>X : number + +import X2 = require("foobarx"); +>X2 : number + +const x: X = X; +>x : number +>X : number +>X : number + +const x2: X2 = X2; +>x2 : number +>X2 : number +>X2 : number + +import B = require("./a"); +>B : typeof B + +const b: B = new B(B.b); +>b : B +>B : B +>new B(B.b) : B +>B : typeof B +>B.b : number +>B : typeof B +>b : number + +import fooLength = require("./b"); +>fooLength : number + +fooLength + 1; +>fooLength + 1 : number +>fooLength : number +>1 : number + +=== tests/cases/compiler/declarations.d.ts === +// This test is just like exportDefaultProperty, but with `export =`. + +declare namespace foo.bar { +>foo : typeof foo +>bar : typeof bar + + export type X = number; +>X : number + + export const X: number; +>X : number +} + +declare module "foobar" { + export = foo.bar; +>foo.bar : typeof foo.bar +>foo : typeof foo +>bar : typeof foo.bar +} + +declare module "foobarx" { + export = foo.bar.X; +>foo.bar.X : number +>foo.bar : typeof foo.bar +>foo : typeof foo +>bar : typeof foo.bar +>X : number +} + +=== tests/cases/compiler/a.ts === +namespace A { +>A : typeof A + + export class B { constructor(b: number) {} } +>B : B +>b : number + + export namespace B { export const b: number = 0; } +>B : typeof B +>b : number +>0 : number +} +export = A.B; +>A.B : typeof A.B +>A : typeof A +>B : typeof A.B + +=== tests/cases/compiler/b.ts === +export = "foo".length; +>"foo".length : number +>"foo" : string +>length : number + diff --git a/tests/baselines/reference/exportToString.js b/tests/baselines/reference/exportToString.js new file mode 100644 index 00000000000..190d1693c24 --- /dev/null +++ b/tests/baselines/reference/exportToString.js @@ -0,0 +1,9 @@ +//// [exportToString.ts] +const toString = 0; +export { toString }; + + +//// [exportToString.js] +"use strict"; +var toString = 0; +exports.toString = toString; diff --git a/tests/baselines/reference/exportToString.symbols b/tests/baselines/reference/exportToString.symbols new file mode 100644 index 00000000000..ce5446317a4 --- /dev/null +++ b/tests/baselines/reference/exportToString.symbols @@ -0,0 +1,7 @@ +=== tests/cases/compiler/exportToString.ts === +const toString = 0; +>toString : Symbol(toString, Decl(exportToString.ts, 0, 5)) + +export { toString }; +>toString : Symbol(toString, Decl(exportToString.ts, 1, 8)) + diff --git a/tests/baselines/reference/exportToString.types b/tests/baselines/reference/exportToString.types new file mode 100644 index 00000000000..17037852f3b --- /dev/null +++ b/tests/baselines/reference/exportToString.types @@ -0,0 +1,8 @@ +=== tests/cases/compiler/exportToString.ts === +const toString = 0; +>toString : number +>0 : number + +export { toString }; +>toString : number + diff --git a/tests/baselines/reference/implicitConstParameters.errors.txt b/tests/baselines/reference/implicitConstParameters.errors.txt new file mode 100644 index 00000000000..95ff60d71f8 --- /dev/null +++ b/tests/baselines/reference/implicitConstParameters.errors.txt @@ -0,0 +1,65 @@ +tests/cases/compiler/implicitConstParameters.ts(39,27): error TS2532: Object is possibly 'undefined'. +tests/cases/compiler/implicitConstParameters.ts(45,27): error TS2532: Object is possibly 'undefined'. + + +==== tests/cases/compiler/implicitConstParameters.ts (2 errors) ==== + + function doSomething(cb: () => void) { + cb(); + } + + function fn(x: number | string) { + if (typeof x === 'number') { + doSomething(() => x.toFixed()); + } + } + + function f1(x: string | undefined) { + if (!x) { + return; + } + doSomething(() => x.length); + } + + function f2(x: string | undefined) { + if (x) { + doSomething(() => { + doSomething(() => x.length); + }); + } + } + + function f3(x: string | undefined) { + inner(); + function inner() { + if (x) { + doSomething(() => x.length); + } + } + } + + function f4(x: string | undefined) { + x = "abc"; // causes x to be considered non-const + if (x) { + doSomething(() => x.length); + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + } + + function f5(x: string | undefined) { + if (x) { + doSomething(() => x.length); + ~ +!!! error TS2532: Object is possibly 'undefined'. + } + x = "abc"; // causes x to be considered non-const + } + + + function f6(x: string | undefined) { + const y = x || ""; + if (x) { + doSomething(() => y.length); + } + } \ No newline at end of file diff --git a/tests/baselines/reference/implicitConstParameters.js b/tests/baselines/reference/implicitConstParameters.js new file mode 100644 index 00000000000..a5faf5b1253 --- /dev/null +++ b/tests/baselines/reference/implicitConstParameters.js @@ -0,0 +1,106 @@ +//// [implicitConstParameters.ts] + +function doSomething(cb: () => void) { + cb(); +} + +function fn(x: number | string) { + if (typeof x === 'number') { + doSomething(() => x.toFixed()); + } +} + +function f1(x: string | undefined) { + if (!x) { + return; + } + doSomething(() => x.length); +} + +function f2(x: string | undefined) { + if (x) { + doSomething(() => { + doSomething(() => x.length); + }); + } +} + +function f3(x: string | undefined) { + inner(); + function inner() { + if (x) { + doSomething(() => x.length); + } + } +} + +function f4(x: string | undefined) { + x = "abc"; // causes x to be considered non-const + if (x) { + doSomething(() => x.length); + } +} + +function f5(x: string | undefined) { + if (x) { + doSomething(() => x.length); + } + x = "abc"; // causes x to be considered non-const +} + + +function f6(x: string | undefined) { + const y = x || ""; + if (x) { + doSomething(() => y.length); + } +} + +//// [implicitConstParameters.js] +function doSomething(cb) { + cb(); +} +function fn(x) { + if (typeof x === 'number') { + doSomething(function () { return x.toFixed(); }); + } +} +function f1(x) { + if (!x) { + return; + } + doSomething(function () { return x.length; }); +} +function f2(x) { + if (x) { + doSomething(function () { + doSomething(function () { return x.length; }); + }); + } +} +function f3(x) { + inner(); + function inner() { + if (x) { + doSomething(function () { return x.length; }); + } + } +} +function f4(x) { + x = "abc"; // causes x to be considered non-const + if (x) { + doSomething(function () { return x.length; }); + } +} +function f5(x) { + if (x) { + doSomething(function () { return x.length; }); + } + x = "abc"; // causes x to be considered non-const +} +function f6(x) { + var y = x || ""; + if (x) { + doSomething(function () { return y.length; }); + } +} diff --git a/tests/baselines/reference/indexWithUndefinedAndNull.js b/tests/baselines/reference/indexWithUndefinedAndNull.js new file mode 100644 index 00000000000..f885323efae --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNull.js @@ -0,0 +1,22 @@ +//// [indexWithUndefinedAndNull.ts] +interface N { + [n: number]: string; +} +interface S { + [s: string]: number; +} +let n: N; +let s: S; +let str: string = n[undefined]; +str = n[null]; +let num: number = s[undefined]; +num = s[null]; + + +//// [indexWithUndefinedAndNull.js] +var n; +var s; +var str = n[undefined]; +str = n[null]; +var num = s[undefined]; +num = s[null]; diff --git a/tests/baselines/reference/indexWithUndefinedAndNull.symbols b/tests/baselines/reference/indexWithUndefinedAndNull.symbols new file mode 100644 index 00000000000..15e8bbdbf41 --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNull.symbols @@ -0,0 +1,39 @@ +=== tests/cases/compiler/indexWithUndefinedAndNull.ts === +interface N { +>N : Symbol(N, Decl(indexWithUndefinedAndNull.ts, 0, 0)) + + [n: number]: string; +>n : Symbol(n, Decl(indexWithUndefinedAndNull.ts, 1, 5)) +} +interface S { +>S : Symbol(S, Decl(indexWithUndefinedAndNull.ts, 2, 1)) + + [s: string]: number; +>s : Symbol(s, Decl(indexWithUndefinedAndNull.ts, 4, 5)) +} +let n: N; +>n : Symbol(n, Decl(indexWithUndefinedAndNull.ts, 6, 3)) +>N : Symbol(N, Decl(indexWithUndefinedAndNull.ts, 0, 0)) + +let s: S; +>s : Symbol(s, Decl(indexWithUndefinedAndNull.ts, 7, 3)) +>S : Symbol(S, Decl(indexWithUndefinedAndNull.ts, 2, 1)) + +let str: string = n[undefined]; +>str : Symbol(str, Decl(indexWithUndefinedAndNull.ts, 8, 3)) +>n : Symbol(n, Decl(indexWithUndefinedAndNull.ts, 6, 3)) +>undefined : Symbol(undefined) + +str = n[null]; +>str : Symbol(str, Decl(indexWithUndefinedAndNull.ts, 8, 3)) +>n : Symbol(n, Decl(indexWithUndefinedAndNull.ts, 6, 3)) + +let num: number = s[undefined]; +>num : Symbol(num, Decl(indexWithUndefinedAndNull.ts, 10, 3)) +>s : Symbol(s, Decl(indexWithUndefinedAndNull.ts, 7, 3)) +>undefined : Symbol(undefined) + +num = s[null]; +>num : Symbol(num, Decl(indexWithUndefinedAndNull.ts, 10, 3)) +>s : Symbol(s, Decl(indexWithUndefinedAndNull.ts, 7, 3)) + diff --git a/tests/baselines/reference/indexWithUndefinedAndNull.types b/tests/baselines/reference/indexWithUndefinedAndNull.types new file mode 100644 index 00000000000..07b6050503a --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNull.types @@ -0,0 +1,47 @@ +=== tests/cases/compiler/indexWithUndefinedAndNull.ts === +interface N { +>N : N + + [n: number]: string; +>n : number +} +interface S { +>S : S + + [s: string]: number; +>s : string +} +let n: N; +>n : N +>N : N + +let s: S; +>s : S +>S : S + +let str: string = n[undefined]; +>str : string +>n[undefined] : string +>n : N +>undefined : undefined + +str = n[null]; +>str = n[null] : string +>str : string +>n[null] : string +>n : N +>null : null + +let num: number = s[undefined]; +>num : number +>s[undefined] : number +>s : S +>undefined : undefined + +num = s[null]; +>num = s[null] : number +>num : number +>s[null] : number +>s : S +>null : null + diff --git a/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.errors.txt b/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.errors.txt new file mode 100644 index 00000000000..a1fce75c54e --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.errors.txt @@ -0,0 +1,40 @@ +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(9,19): error TS2454: Variable 'n' is used before being assigned. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(9,19): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(10,7): error TS2454: Variable 'n' is used before being assigned. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(10,7): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(11,19): error TS2454: Variable 's' is used before being assigned. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(11,19): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(12,7): error TS2454: Variable 's' is used before being assigned. +tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts(12,7): error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + + +==== tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts (8 errors) ==== + interface N { + [n: number]: string; + } + interface S { + [s: string]: number; + } + let n: N; + let s: S; + let str: string = n[undefined]; + ~ +!!! error TS2454: Variable 'n' is used before being assigned. + ~~~~~~~~~~~~ +!!! error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + str = n[null]; + ~ +!!! error TS2454: Variable 'n' is used before being assigned. + ~~~~~~~ +!!! error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + let num: number = s[undefined]; + ~ +!!! error TS2454: Variable 's' is used before being assigned. + ~~~~~~~~~~~~ +!!! error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + num = s[null]; + ~ +!!! error TS2454: Variable 's' is used before being assigned. + ~~~~~~~ +!!! error TS2342: An index expression argument must be of type 'string', 'number', 'symbol', or 'any'. + \ No newline at end of file diff --git a/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.js b/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.js new file mode 100644 index 00000000000..9300b7c0549 --- /dev/null +++ b/tests/baselines/reference/indexWithUndefinedAndNullStrictNullChecks.js @@ -0,0 +1,22 @@ +//// [indexWithUndefinedAndNullStrictNullChecks.ts] +interface N { + [n: number]: string; +} +interface S { + [s: string]: number; +} +let n: N; +let s: S; +let str: string = n[undefined]; +str = n[null]; +let num: number = s[undefined]; +num = s[null]; + + +//// [indexWithUndefinedAndNullStrictNullChecks.js] +var n; +var s; +var str = n[undefined]; +str = n[null]; +var num = s[undefined]; +num = s[null]; diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.js b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.js new file mode 100644 index 00000000000..97be2137722 --- /dev/null +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.js @@ -0,0 +1,172 @@ +//// [instanceofWithStructurallyIdenticalTypes.ts] +// Repro from #7271 + +class C1 { item: string } +class C2 { item: string[] } +class C3 { item: string } + +function foo1(x: C1 | C2 | C3): string { + if (x instanceof C1) { + return x.item; + } + else if (x instanceof C2) { + return x.item[0]; + } + else if (x instanceof C3) { + return x.item; + } + return "error"; +} + +function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 } +function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 } +function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 } + +function foo2(x: C1 | C2 | C3): string { + if (isC1(x)) { + return x.item; + } + else if (isC2(x)) { + return x.item[0]; + } + else if (isC3(x)) { + return x.item; + } + return "error"; +} + +// More tests + +class A { a: string } +class A1 extends A { } +class A2 { a: string } +class B extends A { b: string } + +function goo(x: A) { + if (x instanceof A) { + x; // A + } + else { + x; // never + } + if (x instanceof A1) { + x; // A1 + } + else { + x; // A + } + if (x instanceof A2) { + x; // A2 + } + else { + x; // A + } + if (x instanceof B) { + x; // B + } + else { + x; // A + } +} + + +//// [instanceofWithStructurallyIdenticalTypes.js] +// Repro from #7271 +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var C1 = (function () { + function C1() { + } + return C1; +}()); +var C2 = (function () { + function C2() { + } + return C2; +}()); +var C3 = (function () { + function C3() { + } + return C3; +}()); +function foo1(x) { + if (x instanceof C1) { + return x.item; + } + else if (x instanceof C2) { + return x.item[0]; + } + else if (x instanceof C3) { + return x.item; + } + return "error"; +} +function isC1(c) { return c instanceof C1; } +function isC2(c) { return c instanceof C2; } +function isC3(c) { return c instanceof C3; } +function foo2(x) { + if (isC1(x)) { + return x.item; + } + else if (isC2(x)) { + return x.item[0]; + } + else if (isC3(x)) { + return x.item; + } + return "error"; +} +// More tests +var A = (function () { + function A() { + } + return A; +}()); +var A1 = (function (_super) { + __extends(A1, _super); + function A1() { + _super.apply(this, arguments); + } + return A1; +}(A)); +var A2 = (function () { + function A2() { + } + return A2; +}()); +var B = (function (_super) { + __extends(B, _super); + function B() { + _super.apply(this, arguments); + } + return B; +}(A)); +function goo(x) { + if (x instanceof A) { + x; // A + } + else { + x; // never + } + if (x instanceof A1) { + x; // A1 + } + else { + x; // A + } + if (x instanceof A2) { + x; // A2 + } + else { + x; // A + } + if (x instanceof B) { + x; // B + } + else { + x; // A + } +} diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols new file mode 100644 index 00000000000..f2eb40eea47 --- /dev/null +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.symbols @@ -0,0 +1,192 @@ +=== tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts === +// Repro from #7271 + +class C1 { item: string } +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) + +class C2 { item: string[] } +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) + +class C3 { item: string } +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) + +function foo1(x: C1 | C2 | C3): string { +>foo1 : Symbol(foo1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 25)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) + + if (x instanceof C1) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) + + return x.item; +>x.item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) + } + else if (x instanceof C2) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) + + return x.item[0]; +>x.item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) + } + else if (x instanceof C3) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) + + return x.item; +>x.item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 6, 14)) +>item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) + } + return "error"; +} + +function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 } +>isC1 : Symbol(isC1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 17, 1)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) + +function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 } +>isC2 : Symbol(isC2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 66)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 14)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 14)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) + +function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 } +>isC3 : Symbol(isC3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 66)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 21, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 21, 14)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) +>c : Symbol(c, Decl(instanceofWithStructurallyIdenticalTypes.ts, 21, 14)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) + +function foo2(x: C1 | C2 | C3): string { +>foo2 : Symbol(foo2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 21, 66)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) +>C1 : Symbol(C1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 0, 0)) +>C2 : Symbol(C2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 25)) +>C3 : Symbol(C3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 27)) + + if (isC1(x)) { +>isC1 : Symbol(isC1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 17, 1)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) + + return x.item; +>x.item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) +>item : Symbol(C1.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 2, 10)) + } + else if (isC2(x)) { +>isC2 : Symbol(isC2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 19, 66)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) + + return x.item[0]; +>x.item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) +>item : Symbol(C2.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 3, 10)) + } + else if (isC3(x)) { +>isC3 : Symbol(isC3, Decl(instanceofWithStructurallyIdenticalTypes.ts, 20, 66)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) + + return x.item; +>x.item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 23, 14)) +>item : Symbol(C3.item, Decl(instanceofWithStructurallyIdenticalTypes.ts, 4, 10)) + } + return "error"; +} + +// More tests + +class A { a: string } +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) +>a : Symbol(A.a, Decl(instanceofWithStructurallyIdenticalTypes.ts, 38, 9)) + +class A1 extends A { } +>A1 : Symbol(A1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 38, 21)) +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) + +class A2 { a: string } +>A2 : Symbol(A2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 39, 22)) +>a : Symbol(A2.a, Decl(instanceofWithStructurallyIdenticalTypes.ts, 40, 10)) + +class B extends A { b: string } +>B : Symbol(B, Decl(instanceofWithStructurallyIdenticalTypes.ts, 40, 22)) +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) +>b : Symbol(B.b, Decl(instanceofWithStructurallyIdenticalTypes.ts, 41, 19)) + +function goo(x: A) { +>goo : Symbol(goo, Decl(instanceofWithStructurallyIdenticalTypes.ts, 41, 31)) +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) + + if (x instanceof A) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>A : Symbol(A, Decl(instanceofWithStructurallyIdenticalTypes.ts, 34, 1)) + + x; // A +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + else { + x; // never +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + if (x instanceof A1) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>A1 : Symbol(A1, Decl(instanceofWithStructurallyIdenticalTypes.ts, 38, 21)) + + x; // A1 +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + else { + x; // A +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + if (x instanceof A2) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>A2 : Symbol(A2, Decl(instanceofWithStructurallyIdenticalTypes.ts, 39, 22)) + + x; // A2 +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + else { + x; // A +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + if (x instanceof B) { +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) +>B : Symbol(B, Decl(instanceofWithStructurallyIdenticalTypes.ts, 40, 22)) + + x; // B +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } + else { + x; // A +>x : Symbol(x, Decl(instanceofWithStructurallyIdenticalTypes.ts, 43, 13)) + } +} + diff --git a/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types new file mode 100644 index 00000000000..d98a6025796 --- /dev/null +++ b/tests/baselines/reference/instanceofWithStructurallyIdenticalTypes.types @@ -0,0 +1,211 @@ +=== tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts === +// Repro from #7271 + +class C1 { item: string } +>C1 : C1 +>item : string + +class C2 { item: string[] } +>C2 : C2 +>item : string[] + +class C3 { item: string } +>C3 : C3 +>item : string + +function foo1(x: C1 | C2 | C3): string { +>foo1 : (x: C1 | C2 | C3) => string +>x : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 + + if (x instanceof C1) { +>x instanceof C1 : boolean +>x : C1 | C2 | C3 +>C1 : typeof C1 + + return x.item; +>x.item : string +>x : C1 +>item : string + } + else if (x instanceof C2) { +>x instanceof C2 : boolean +>x : C2 | C3 +>C2 : typeof C2 + + return x.item[0]; +>x.item[0] : string +>x.item : string[] +>x : C2 +>item : string[] +>0 : number + } + else if (x instanceof C3) { +>x instanceof C3 : boolean +>x : C3 +>C3 : typeof C3 + + return x.item; +>x.item : string +>x : C3 +>item : string + } + return "error"; +>"error" : string +} + +function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 } +>isC1 : (c: C1 | C2 | C3) => c is C1 +>c : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 +>c : any +>C1 : C1 +>c instanceof C1 : boolean +>c : C1 | C2 | C3 +>C1 : typeof C1 + +function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 } +>isC2 : (c: C1 | C2 | C3) => c is C2 +>c : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 +>c : any +>C2 : C2 +>c instanceof C2 : boolean +>c : C1 | C2 | C3 +>C2 : typeof C2 + +function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 } +>isC3 : (c: C1 | C2 | C3) => c is C3 +>c : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 +>c : any +>C3 : C3 +>c instanceof C3 : boolean +>c : C1 | C2 | C3 +>C3 : typeof C3 + +function foo2(x: C1 | C2 | C3): string { +>foo2 : (x: C1 | C2 | C3) => string +>x : C1 | C2 | C3 +>C1 : C1 +>C2 : C2 +>C3 : C3 + + if (isC1(x)) { +>isC1(x) : boolean +>isC1 : (c: C1 | C2 | C3) => c is C1 +>x : C1 | C2 | C3 + + return x.item; +>x.item : string +>x : C1 +>item : string + } + else if (isC2(x)) { +>isC2(x) : boolean +>isC2 : (c: C1 | C2 | C3) => c is C2 +>x : C2 | C3 + + return x.item[0]; +>x.item[0] : string +>x.item : string[] +>x : C2 +>item : string[] +>0 : number + } + else if (isC3(x)) { +>isC3(x) : boolean +>isC3 : (c: C1 | C2 | C3) => c is C3 +>x : C3 + + return x.item; +>x.item : string +>x : C3 +>item : string + } + return "error"; +>"error" : string +} + +// More tests + +class A { a: string } +>A : A +>a : string + +class A1 extends A { } +>A1 : A1 +>A : A + +class A2 { a: string } +>A2 : A2 +>a : string + +class B extends A { b: string } +>B : B +>A : A +>b : string + +function goo(x: A) { +>goo : (x: A) => void +>x : A +>A : A + + if (x instanceof A) { +>x instanceof A : boolean +>x : A +>A : typeof A + + x; // A +>x : A + } + else { + x; // never +>x : never + } + if (x instanceof A1) { +>x instanceof A1 : boolean +>x : A +>A1 : typeof A1 + + x; // A1 +>x : A1 + } + else { + x; // A +>x : A + } + if (x instanceof A2) { +>x instanceof A2 : boolean +>x : A +>A2 : typeof A2 + + x; // A2 +>x : A2 + } + else { + x; // A +>x : A + } + if (x instanceof B) { +>x instanceof B : boolean +>x : A +>B : typeof B + + x; // B +>x : B + } + else { + x; // A +>x : A + } +} + diff --git a/tests/baselines/reference/library-reference-13.trace.json b/tests/baselines/reference/library-reference-13.trace.json index d8dfb57c2a6..a23f0ef0ca5 100644 --- a/tests/baselines/reference/library-reference-13.trace.json +++ b/tests/baselines/reference/library-reference-13.trace.json @@ -1,5 +1,5 @@ [ - "======== Resolving type reference directive 'jquery', containing file '/a/b/__inferred type names__.ts', root directory '/a/types'. ========", + "======== Resolving type reference directive 'jquery', containing file '/__inferred type names__.ts', root directory '/a/types'. ========", "Resolving with primary search path '/a/types'", "File '/a/types/jquery/package.json' does not exist.", "File '/a/types/jquery/index.d.ts' exist - use it as a name resolution result.", diff --git a/tests/baselines/reference/library-reference-14.trace.json b/tests/baselines/reference/library-reference-14.trace.json index d8dfb57c2a6..fb3a2bb7da4 100644 --- a/tests/baselines/reference/library-reference-14.trace.json +++ b/tests/baselines/reference/library-reference-14.trace.json @@ -1,5 +1,5 @@ [ - "======== Resolving type reference directive 'jquery', containing file '/a/b/__inferred type names__.ts', root directory '/a/types'. ========", + "======== Resolving type reference directive 'jquery', containing file '/a/__inferred type names__.ts', root directory '/a/types'. ========", "Resolving with primary search path '/a/types'", "File '/a/types/jquery/package.json' does not exist.", "File '/a/types/jquery/index.d.ts' exist - use it as a name resolution result.", diff --git a/tests/baselines/reference/library-reference-15.trace.json b/tests/baselines/reference/library-reference-15.trace.json index 3e9d7dba1d2..e23517976b0 100644 --- a/tests/baselines/reference/library-reference-15.trace.json +++ b/tests/baselines/reference/library-reference-15.trace.json @@ -1,5 +1,5 @@ [ - "======== Resolving type reference directive 'jquery', containing file '/a/b/__inferred type names__.ts', root directory 'types'. ========", + "======== Resolving type reference directive 'jquery', containing file '/a/__inferred type names__.ts', root directory 'types'. ========", "Resolving with primary search path 'types'", "File 'types/jquery/package.json' does not exist.", "File 'types/jquery/index.d.ts' exist - use it as a name resolution result.", diff --git a/tests/baselines/reference/library-reference-2.trace.json b/tests/baselines/reference/library-reference-2.trace.json index c26f7d2763d..64cdd809183 100644 --- a/tests/baselines/reference/library-reference-2.trace.json +++ b/tests/baselines/reference/library-reference-2.trace.json @@ -5,7 +5,7 @@ "'package.json' has 'types' field 'jquery.d.ts' that references '/types/jquery/jquery.d.ts'.", "File '/types/jquery/jquery.d.ts' exist - use it as a name resolution result.", "======== Type reference directive 'jquery' was successfully resolved to '/types/jquery/jquery.d.ts', primary: true. ========", - "======== Resolving type reference directive 'jquery', containing file '/__inferred type names__.ts', root directory '/types'. ========", + "======== Resolving type reference directive 'jquery', containing file 'test/__inferred type names__.ts', root directory '/types'. ========", "Resolving with primary search path '/types'", "Found 'package.json' at '/types/jquery/package.json'.", "'package.json' has 'types' field 'jquery.d.ts' that references '/types/jquery/jquery.d.ts'.", diff --git a/tests/baselines/reference/library-reference-5.errors.txt b/tests/baselines/reference/library-reference-5.errors.txt index a3729bc3a99..c23b49c61de 100644 --- a/tests/baselines/reference/library-reference-5.errors.txt +++ b/tests/baselines/reference/library-reference-5.errors.txt @@ -1,4 +1,4 @@ -/node_modules/bar/index.d.ts(1,1): message TS4090: Conflicting library definitions for 'alpha' found at '/node_modules/bar/node_modules/alpha/index.d.ts' and '/node_modules/foo/node_modules/alpha/index.d.ts'. Copy the correct file to the 'typings' folder to resolve this conflict. +/node_modules/bar/index.d.ts(1,1): message TS4090: Conflicting definitions for 'alpha' found at '/node_modules/bar/node_modules/alpha/index.d.ts' and '/node_modules/foo/node_modules/alpha/index.d.ts'. Consider installing a specific version of this library to resolve the conflict. ==== /src/root.ts (0 errors) ==== @@ -18,7 +18,7 @@ ==== /node_modules/bar/index.d.ts (1 errors) ==== /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -!!! message TS4090: Conflicting library definitions for 'alpha' found at '/node_modules/bar/node_modules/alpha/index.d.ts' and '/node_modules/foo/node_modules/alpha/index.d.ts'. Copy the correct file to the 'typings' folder to resolve this conflict. +!!! message TS4090: Conflicting definitions for 'alpha' found at '/node_modules/bar/node_modules/alpha/index.d.ts' and '/node_modules/foo/node_modules/alpha/index.d.ts'. Consider installing a specific version of this library to resolve the conflict. declare var bar: any; ==== /node_modules/bar/node_modules/alpha/index.d.ts (0 errors) ==== diff --git a/tests/baselines/reference/library-reference-6.trace.json b/tests/baselines/reference/library-reference-6.trace.json index 48fb49e6c7f..fd83c1431b4 100644 --- a/tests/baselines/reference/library-reference-6.trace.json +++ b/tests/baselines/reference/library-reference-6.trace.json @@ -4,7 +4,7 @@ "File '/node_modules/@types/alpha/package.json' does not exist.", "File '/node_modules/@types/alpha/index.d.ts' exist - use it as a name resolution result.", "======== Type reference directive 'alpha' was successfully resolved to '/node_modules/@types/alpha/index.d.ts', primary: true. ========", - "======== Resolving type reference directive 'alpha', containing file '/src/__inferred type names__.ts', root directory '/node_modules/@types'. ========", + "======== Resolving type reference directive 'alpha', containing file '/__inferred type names__.ts', root directory '/node_modules/@types'. ========", "Resolving with primary search path '/node_modules/@types'", "File '/node_modules/@types/alpha/package.json' does not exist.", "File '/node_modules/@types/alpha/index.d.ts' exist - use it as a name resolution result.", diff --git a/tests/baselines/reference/narrowExceptionVariableInCatchClause.errors.txt b/tests/baselines/reference/narrowExceptionVariableInCatchClause.errors.txt new file mode 100644 index 00000000000..e435b98050d --- /dev/null +++ b/tests/baselines/reference/narrowExceptionVariableInCatchClause.errors.txt @@ -0,0 +1,33 @@ +tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts(11,17): error TS2339: Property 'doPanic' does not exist on type '{ type: "foo"; dontPanic(): any; }'. +tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts(16,17): error TS2339: Property 'massage' does not exist on type 'Error'. + + +==== tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts (2 errors) ==== + declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); }; + + function tryCatch() { + try { + // do stuff... + } + catch (err) { // err is implicitly 'any' and cannot be annotated + + if (isFooError(err)) { + err.dontPanic(); // OK + err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}' + ~~~~~~~ +!!! error TS2339: Property 'doPanic' does not exist on type '{ type: "foo"; dontPanic(): any; }'. + } + + else if (err instanceof Error) { + err.message; + err.massage; // ERROR: Property 'massage' does not exist on type 'Error' + ~~~~~~~ +!!! error TS2339: Property 'massage' does not exist on type 'Error'. + } + + else { + throw err; + } + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/narrowExceptionVariableInCatchClause.js b/tests/baselines/reference/narrowExceptionVariableInCatchClause.js new file mode 100644 index 00000000000..5808ed76826 --- /dev/null +++ b/tests/baselines/reference/narrowExceptionVariableInCatchClause.js @@ -0,0 +1,44 @@ +//// [narrowExceptionVariableInCatchClause.ts] +declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); }; + +function tryCatch() { + try { + // do stuff... + } + catch (err) { // err is implicitly 'any' and cannot be annotated + + if (isFooError(err)) { + err.dontPanic(); // OK + err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}' + } + + else if (err instanceof Error) { + err.message; + err.massage; // ERROR: Property 'massage' does not exist on type 'Error' + } + + else { + throw err; + } + } +} + + +//// [narrowExceptionVariableInCatchClause.js] +function tryCatch() { + try { + } + catch (err) { + if (isFooError(err)) { + err.dontPanic(); // OK + err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}' + } + else if (err instanceof Error) { + err.message; + err.massage; // ERROR: Property 'massage' does not exist on type 'Error' + } + else { + throw err; + } + } +} diff --git a/tests/baselines/reference/narrowFromAnyWithInstanceof.errors.txt b/tests/baselines/reference/narrowFromAnyWithInstanceof.errors.txt new file mode 100644 index 00000000000..3e152b0faf4 --- /dev/null +++ b/tests/baselines/reference/narrowFromAnyWithInstanceof.errors.txt @@ -0,0 +1,33 @@ +tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts(17,7): error TS2339: Property 'mesage' does not exist on type 'Error'. +tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts(22,7): error TS2339: Property 'getHuors' does not exist on type 'Date'. + + +==== tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts (2 errors) ==== + declare var x: any; + + if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; + } + + if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); + } + + if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object' + x.message; + x.mesage; + ~~~~~~ +!!! error TS2339: Property 'mesage' does not exist on type 'Error'. + } + + if (x instanceof Date) { + x.getDate(); + x.getHuors(); + ~~~~~~~~ +!!! error TS2339: Property 'getHuors' does not exist on type 'Date'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/narrowFromAnyWithInstanceof.js b/tests/baselines/reference/narrowFromAnyWithInstanceof.js new file mode 100644 index 00000000000..4cf1ca174aa --- /dev/null +++ b/tests/baselines/reference/narrowFromAnyWithInstanceof.js @@ -0,0 +1,45 @@ +//// [narrowFromAnyWithInstanceof.ts] +declare var x: any; + +if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} + +if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); +} + +if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object' + x.message; + x.mesage; +} + +if (x instanceof Date) { + x.getDate(); + x.getHuors(); +} + + +//// [narrowFromAnyWithInstanceof.js] +if (x instanceof Function) { + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} +if (x instanceof Object) { + x.method(); + x(); +} +if (x instanceof Error) { + x.message; + x.mesage; +} +if (x instanceof Date) { + x.getDate(); + x.getHuors(); +} diff --git a/tests/baselines/reference/narrowFromAnyWithTypePredicate.errors.txt b/tests/baselines/reference/narrowFromAnyWithTypePredicate.errors.txt new file mode 100644 index 00000000000..94f41becdad --- /dev/null +++ b/tests/baselines/reference/narrowFromAnyWithTypePredicate.errors.txt @@ -0,0 +1,50 @@ +tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(22,7): error TS2339: Property 'method' does not exist on type '{}'. +tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(23,5): error TS2349: Cannot invoke an expression whose type lacks a call signature. +tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(28,7): error TS2339: Property 'mesage' does not exist on type 'Error'. +tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts(33,7): error TS2339: Property 'getHuors' does not exist on type 'Date'. + + +==== tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts (4 errors) ==== + declare var x: any; + declare function isFunction(x): x is Function; + declare function isObject(x): x is Object; + declare function isAnything(x): x is {}; + declare function isError(x): x is Error; + declare function isDate(x): x is Date; + + + if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; + } + + if (isObject(x)) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); + } + + if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {}) + x.method(); + ~~~~~~ +!!! error TS2339: Property 'method' does not exist on type '{}'. + x(); + ~~~ +!!! error TS2349: Cannot invoke an expression whose type lacks a call signature. + } + + if (isError(x)) { + x.message; + x.mesage; + ~~~~~~ +!!! error TS2339: Property 'mesage' does not exist on type 'Error'. + } + + if (isDate(x)) { + x.getDate(); + x.getHuors(); + ~~~~~~~~ +!!! error TS2339: Property 'getHuors' does not exist on type 'Date'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/narrowFromAnyWithTypePredicate.js b/tests/baselines/reference/narrowFromAnyWithTypePredicate.js new file mode 100644 index 00000000000..958a3cfd70d --- /dev/null +++ b/tests/baselines/reference/narrowFromAnyWithTypePredicate.js @@ -0,0 +1,60 @@ +//// [narrowFromAnyWithTypePredicate.ts] +declare var x: any; +declare function isFunction(x): x is Function; +declare function isObject(x): x is Object; +declare function isAnything(x): x is {}; +declare function isError(x): x is Error; +declare function isDate(x): x is Date; + + +if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} + +if (isObject(x)) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); +} + +if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {}) + x.method(); + x(); +} + +if (isError(x)) { + x.message; + x.mesage; +} + +if (isDate(x)) { + x.getDate(); + x.getHuors(); +} + + +//// [narrowFromAnyWithTypePredicate.js] +if (isFunction(x)) { + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} +if (isObject(x)) { + x.method(); + x(); +} +if (isAnything(x)) { + x.method(); + x(); +} +if (isError(x)) { + x.message; + x.mesage; +} +if (isDate(x)) { + x.getDate(); + x.getHuors(); +} diff --git a/tests/baselines/reference/stringLiteralTypesAndTuples01.types b/tests/baselines/reference/stringLiteralTypesAndTuples01.types index 0900d9fc745..10a30a6d75c 100644 --- a/tests/baselines/reference/stringLiteralTypesAndTuples01.types +++ b/tests/baselines/reference/stringLiteralTypesAndTuples01.types @@ -55,5 +55,5 @@ function rawr(dino: RexOrRaptor) { throw "Unexpected " + dino; >"Unexpected " + dino : string >"Unexpected " : string ->dino : "t-rex" +>dino : never } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags01.types b/tests/baselines/reference/stringLiteralTypesAsTags01.types index e00fd18163d..e95a7364f93 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.types @@ -99,8 +99,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : never +>x : never } if (!hasKind(x, "B")) { diff --git a/tests/baselines/reference/stringLiteralTypesAsTags02.types b/tests/baselines/reference/stringLiteralTypesAsTags02.types index fb1632559ea..c984b8a78f9 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags02.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags02.types @@ -93,8 +93,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : never +>x : never } if (!hasKind(x, "B")) { diff --git a/tests/baselines/reference/stringLiteralTypesAsTags03.types b/tests/baselines/reference/stringLiteralTypesAsTags03.types index 05be633813b..fbe71ff07c1 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags03.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags03.types @@ -96,8 +96,8 @@ if (hasKind(x, "A")) { } else { let b = x; ->b : A ->x : A +>b : never +>x : never } if (!hasKind(x, "B")) { diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.js b/tests/baselines/reference/thisInTupleTypeParameterConstraints.js new file mode 100644 index 00000000000..c63fbf61991 --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.js @@ -0,0 +1,29 @@ +//// [thisInTupleTypeParameterConstraints.ts] +/// + +interface Boolean {} +interface IArguments {} +interface Function {} +interface Number {} +interface RegExp {} +interface Object {} +interface String {} + +interface Array { + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; + reduceRight(arg: this): void; + reduce(arg: this): void; + reduce2(arg: this): void; +} + +declare function f number]>(a: T): void; +let x: [(x: number) => number]; +f(x); + + +//// [thisInTupleTypeParameterConstraints.js] +/// +var x; +f(x); diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols b/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols new file mode 100644 index 00000000000..4d2bef22c55 --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.symbols @@ -0,0 +1,66 @@ +=== tests/cases/compiler/thisInTupleTypeParameterConstraints.ts === +/// + +interface Boolean {} +>Boolean : Symbol(Boolean, Decl(thisInTupleTypeParameterConstraints.ts, 0, 0)) + +interface IArguments {} +>IArguments : Symbol(IArguments, Decl(thisInTupleTypeParameterConstraints.ts, 2, 20)) + +interface Function {} +>Function : Symbol(Function, Decl(thisInTupleTypeParameterConstraints.ts, 3, 23)) + +interface Number {} +>Number : Symbol(Number, Decl(thisInTupleTypeParameterConstraints.ts, 4, 21)) + +interface RegExp {} +>RegExp : Symbol(RegExp, Decl(thisInTupleTypeParameterConstraints.ts, 5, 19)) + +interface Object {} +>Object : Symbol(Object, Decl(thisInTupleTypeParameterConstraints.ts, 6, 19)) + +interface String {} +>String : Symbol(String, Decl(thisInTupleTypeParameterConstraints.ts, 7, 19)) + +interface Array { +>Array : Symbol(Array, Decl(thisInTupleTypeParameterConstraints.ts, 8, 19)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 10, 16)) + + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; +>map : Symbol(Array.map, Decl(thisInTupleTypeParameterConstraints.ts, 10, 20)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 13, 8)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 13, 11)) + + reduceRight(arg: this): void; +>reduceRight : Symbol(Array.reduceRight, Decl(thisInTupleTypeParameterConstraints.ts, 13, 28)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 14, 16)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 14, 19)) + + reduce(arg: this): void; +>reduce : Symbol(Array.reduce, Decl(thisInTupleTypeParameterConstraints.ts, 14, 36)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 15, 11)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 15, 14)) + + reduce2(arg: this): void; +>reduce2 : Symbol(Array.reduce2, Decl(thisInTupleTypeParameterConstraints.ts, 15, 31)) +>U : Symbol(U, Decl(thisInTupleTypeParameterConstraints.ts, 16, 12)) +>arg : Symbol(arg, Decl(thisInTupleTypeParameterConstraints.ts, 16, 15)) +} + +declare function f number]>(a: T): void; +>f : Symbol(f, Decl(thisInTupleTypeParameterConstraints.ts, 17, 1)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 19, 19)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 19, 31)) +>a : Symbol(a, Decl(thisInTupleTypeParameterConstraints.ts, 19, 54)) +>T : Symbol(T, Decl(thisInTupleTypeParameterConstraints.ts, 19, 19)) + +let x: [(x: number) => number]; +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 3)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 9)) + +f(x); +>f : Symbol(f, Decl(thisInTupleTypeParameterConstraints.ts, 17, 1)) +>x : Symbol(x, Decl(thisInTupleTypeParameterConstraints.ts, 20, 3)) + diff --git a/tests/baselines/reference/thisInTupleTypeParameterConstraints.types b/tests/baselines/reference/thisInTupleTypeParameterConstraints.types new file mode 100644 index 00000000000..7daafe02bfc --- /dev/null +++ b/tests/baselines/reference/thisInTupleTypeParameterConstraints.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/thisInTupleTypeParameterConstraints.ts === +/// + +interface Boolean {} +>Boolean : Boolean + +interface IArguments {} +>IArguments : IArguments + +interface Function {} +>Function : Function + +interface Number {} +>Number : Number + +interface RegExp {} +>RegExp : RegExp + +interface Object {} +>Object : Object + +interface String {} +>String : String + +interface Array { +>Array : T[] +>T : T + + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; +>map : (arg: this) => void +>U : U +>arg : this + + reduceRight(arg: this): void; +>reduceRight : (arg: this) => void +>U : U +>arg : this + + reduce(arg: this): void; +>reduce : (arg: this) => void +>U : U +>arg : this + + reduce2(arg: this): void; +>reduce2 : (arg: this) => void +>U : U +>arg : this +} + +declare function f number]>(a: T): void; +>f : number]>(a: T) => void +>T : T +>x : number +>a : T +>T : T + +let x: [(x: number) => number]; +>x : [(x: number) => number] +>x : number + +f(x); +>f(x) : void +>f : number]>(a: T) => void +>x : [(x: number) => number] + diff --git a/tests/baselines/reference/tsxAttributeResolution14.errors.txt b/tests/baselines/reference/tsxAttributeResolution14.errors.txt new file mode 100644 index 00000000000..81d42e49059 --- /dev/null +++ b/tests/baselines/reference/tsxAttributeResolution14.errors.txt @@ -0,0 +1,38 @@ +tests/cases/conformance/jsx/file.tsx(14,28): error TS2322: Type 'number' is not assignable to type 'string'. +tests/cases/conformance/jsx/file.tsx(16,28): error TS2322: Type 'boolean' is not assignable to type 'string | number'. + + +==== tests/cases/conformance/jsx/react.d.ts (0 errors) ==== + + declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any; + } + interface ElementAttributesProperty { prop: any } + } + +==== tests/cases/conformance/jsx/file.tsx (2 errors) ==== + + interface IProps { + primaryText: string, + [propName: string]: string | number + } + + function VerticalNavMenuItem(prop: IProps) { + return
props.primaryText
+ } + + function VerticalNav() { + return ( +
+ // error + ~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'string'. + // ok + // error + ~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS2322: Type 'boolean' is not assignable to type 'string | number'. +
+ ) + } \ No newline at end of file diff --git a/tests/baselines/reference/tsxAttributeResolution14.js b/tests/baselines/reference/tsxAttributeResolution14.js new file mode 100644 index 00000000000..d920179458c --- /dev/null +++ b/tests/baselines/reference/tsxAttributeResolution14.js @@ -0,0 +1,47 @@ +//// [tests/cases/conformance/jsx/tsxAttributeResolution14.tsx] //// + +//// [react.d.ts] + +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any; + } + interface ElementAttributesProperty { prop: any } +} + +//// [file.tsx] + +interface IProps { + primaryText: string, + [propName: string]: string | number +} + +function VerticalNavMenuItem(prop: IProps) { + return
props.primaryText
+} + +function VerticalNav() { + return ( +
+ // error + // ok + // error +
+ ) +} + +//// [file.jsx] +function VerticalNavMenuItem(prop) { + return
props.primaryText
; +} +function VerticalNav() { + return (
+ // error + // error + // ok + // ok + // error + // error +
); +} diff --git a/tests/baselines/reference/tsxAttributeResolution5.errors.txt b/tests/baselines/reference/tsxAttributeResolution5.errors.txt index d7aa46bc05a..42dabc741ab 100644 --- a/tests/baselines/reference/tsxAttributeResolution5.errors.txt +++ b/tests/baselines/reference/tsxAttributeResolution5.errors.txt @@ -2,9 +2,10 @@ tests/cases/conformance/jsx/file.tsx(21,16): error TS2606: Property 'x' of JSX s Type 'number' is not assignable to type 'string'. tests/cases/conformance/jsx/file.tsx(25,9): error TS2324: Property 'x' is missing in type 'Attribs1'. tests/cases/conformance/jsx/file.tsx(29,1): error TS2324: Property 'x' is missing in type 'Attribs1'. +tests/cases/conformance/jsx/file.tsx(30,1): error TS2324: Property 'toString' is missing in type 'Attribs2'. -==== tests/cases/conformance/jsx/file.tsx (3 errors) ==== +==== tests/cases/conformance/jsx/file.tsx (4 errors) ==== declare module JSX { interface Element { } interface IntrinsicElements { @@ -41,5 +42,7 @@ tests/cases/conformance/jsx/file.tsx(29,1): error TS2324: Property 'x' is missin ; // Error, missing x ~~~~~~~~~~~~~~~~~ !!! error TS2324: Property 'x' is missing in type 'Attribs1'. - ; // OK + ; // Error, missing toString + ~~~~~~~~~~~~~~~~~ +!!! error TS2324: Property 'toString' is missing in type 'Attribs2'. \ No newline at end of file diff --git a/tests/baselines/reference/tsxAttributeResolution5.js b/tests/baselines/reference/tsxAttributeResolution5.js index abfb54e000c..4bde09074b5 100644 --- a/tests/baselines/reference/tsxAttributeResolution5.js +++ b/tests/baselines/reference/tsxAttributeResolution5.js @@ -28,7 +28,7 @@ function make3 (obj: T) { ; // Error, missing x -; // OK +; // Error, missing toString //// [file.jsx] @@ -42,4 +42,4 @@ function make3(obj) { return ; // Error, missing x } ; // Error, missing x -; // OK +; // Error, missing toString diff --git a/tests/baselines/reference/typeGuardTautologicalConsistiency.types b/tests/baselines/reference/typeGuardTautologicalConsistiency.types index f86a9e66fa2..0a6524dd26f 100644 --- a/tests/baselines/reference/typeGuardTautologicalConsistiency.types +++ b/tests/baselines/reference/typeGuardTautologicalConsistiency.types @@ -15,7 +15,7 @@ if (typeof stringOrNumber === "number") { >"number" : "number" stringOrNumber; ->stringOrNumber : string +>stringOrNumber : never } } @@ -31,6 +31,6 @@ if (typeof stringOrNumber === "number" && typeof stringOrNumber !== "number") { >"number" : "number" stringOrNumber; ->stringOrNumber : string +>stringOrNumber : never } diff --git a/tests/baselines/reference/typeGuardTypeOfUndefined.types b/tests/baselines/reference/typeGuardTypeOfUndefined.types index 8e9928896a1..520d045d9e0 100644 --- a/tests/baselines/reference/typeGuardTypeOfUndefined.types +++ b/tests/baselines/reference/typeGuardTypeOfUndefined.types @@ -47,7 +47,7 @@ function test2(a: any) { >"boolean" : "boolean" a; ->a : boolean +>a : never } else { a; @@ -129,7 +129,7 @@ function test5(a: boolean | void) { } else { a; ->a : void +>a : never } } else { @@ -188,7 +188,7 @@ function test7(a: boolean | void) { } else { a; ->a : void +>a : never } } diff --git a/tests/baselines/reference/typeGuardsAsAssertions.types b/tests/baselines/reference/typeGuardsAsAssertions.types index 34d3b296a48..661ce9fbc55 100644 --- a/tests/baselines/reference/typeGuardsAsAssertions.types +++ b/tests/baselines/reference/typeGuardsAsAssertions.types @@ -193,10 +193,10 @@ function f1() { >x : undefined x; // string | number (guard as assertion) ->x : string | number +>x : never } x; // string | number | undefined ->x : string | number | undefined +>x : undefined } function f2() { @@ -216,10 +216,10 @@ function f2() { >"string" : "string" x; // string (guard as assertion) ->x : string +>x : never } x; // string | undefined ->x : string | undefined +>x : undefined } function f3() { @@ -239,7 +239,7 @@ function f3() { return; } x; // string | number (guard as assertion) ->x : string | number +>x : never } function f4() { @@ -281,7 +281,7 @@ function f5(x: string | number) { >"number" : "number" x; // number (guard as assertion) ->x : number +>x : never } else { x; // string | number diff --git a/tests/baselines/reference/typeGuardsInIfStatement.errors.txt b/tests/baselines/reference/typeGuardsInIfStatement.errors.txt index 984ca454e76..cd9ae40932a 100644 --- a/tests/baselines/reference/typeGuardsInIfStatement.errors.txt +++ b/tests/baselines/reference/typeGuardsInIfStatement.errors.txt @@ -1,9 +1,10 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(22,10): error TS2354: No best common type exists among return expressions. tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(31,10): error TS2354: No best common type exists among return expressions. tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(49,10): error TS2354: No best common type exists among return expressions. +tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(139,17): error TS2339: Property 'toString' does not exist on type 'never'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts (3 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts (4 errors) ==== // In the true branch statement of an 'if' statement, // the type of a variable or parameter is narrowed by any type guard in the 'if' condition when true. // In the false branch statement of an 'if' statement, @@ -149,5 +150,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsInIfStatement.ts(49,10) return typeof x === "number" ? x.toString() // number : x.toString(); // boolean | string + ~~~~~~~~ +!!! error TS2339: Property 'toString' does not exist on type 'never'. } } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt index c574118f91d..b67f7328856 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.errors.txt @@ -1,16 +1,26 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(12,10): error TS2339: Property 'bar' does not exist on type 'A'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(18,10): error TS2339: Property 'bar' does not exist on type 'A'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(33,5): error TS2322: Type 'string' is not assignable to type 'number'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(34,10): error TS2339: Property 'bar' does not exist on type 'B'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(41,10): error TS2339: Property 'bar' does not exist on type 'B'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(66,10): error TS2339: Property 'bar2' does not exist on type 'C1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(72,10): error TS2339: Property 'bar1' does not exist on type 'C1 | C2'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(73,10): error TS2339: Property 'bar2' does not exist on type 'C1 | C2'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(85,10): error TS2339: Property 'bar' does not exist on type 'D'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(91,10): error TS2339: Property 'bar' does not exist on type 'D'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(112,10): error TS2339: Property 'bar2' does not exist on type 'E1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(118,11): error TS2339: Property 'bar1' does not exist on type 'E1 | E2'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(119,11): error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(134,11): error TS2339: Property 'foo' does not exist on type 'string | F'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(135,11): error TS2339: Property 'bar' does not exist on type 'string | F'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(160,11): error TS2339: Property 'foo2' does not exist on type 'G1'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(166,11): error TS2339: Property 'foo2' does not exist on type 'G1'. tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(182,11): error TS2339: Property 'bar' does not exist on type 'H'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(187,11): error TS2339: Property 'foo1' does not exist on type 'H'. +tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts(188,11): error TS2339: Property 'foo2' does not exist on type 'H'. -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (10 errors) ==== +==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts (20 errors) ==== interface AConstructor { new (): A; } @@ -28,9 +38,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj2: any; - if (obj2 instanceof A) { // can't narrow type from 'any' + if (obj2 instanceof A) { obj2.foo; obj2.bar; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'A'. } // a construct signature with generics @@ -54,10 +66,12 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj4: any; - if (obj4 instanceof B) { // can't narrow type from 'any' + if (obj4 instanceof B) { obj4.foo = "str"; obj4.foo = 1; obj4.bar = "str"; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'B'. } // has multiple construct signature @@ -88,10 +102,14 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj6: any; - if (obj6 instanceof C) { // can't narrow type from 'any' + if (obj6 instanceof C) { obj6.foo; obj6.bar1; + ~~~~ +!!! error TS2339: Property 'bar1' does not exist on type 'C1 | C2'. obj6.bar2; + ~~~~ +!!! error TS2339: Property 'bar2' does not exist on type 'C1 | C2'. } // with object type literal @@ -109,9 +127,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj8: any; - if (obj8 instanceof D) { // can't narrow type from 'any' + if (obj8 instanceof D) { obj8.foo; obj8.bar; + ~~~ +!!! error TS2339: Property 'bar' does not exist on type 'D'. } // a construct signature that returns a union type @@ -138,10 +158,14 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj10: any; - if (obj10 instanceof E) { // can't narrow type from 'any' + if (obj10 instanceof E) { obj10.foo; obj10.bar1; + ~~~~ +!!! error TS2339: Property 'bar1' does not exist on type 'E1 | E2'. obj10.bar2; + ~~~~ +!!! error TS2339: Property 'bar2' does not exist on type 'E1 | E2'. } // a construct signature that returns any @@ -165,7 +189,7 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj12: any; - if (obj12 instanceof F) { // can't narrow type from 'any' + if (obj12 instanceof F) { obj12.foo; obj12.bar; } @@ -192,9 +216,11 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj14: any; - if (obj14 instanceof G) { // can't narrow type from 'any' + if (obj14 instanceof G) { obj14.foo1; obj14.foo2; + ~~~~ +!!! error TS2339: Property 'foo2' does not exist on type 'G1'. } // a type with a prototype that has any type @@ -216,8 +242,24 @@ tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstru } var obj16: any; - if (obj16 instanceof H) { // can't narrow type from 'any' + if (obj16 instanceof H) { obj16.foo1; + ~~~~ +!!! error TS2339: Property 'foo1' does not exist on type 'H'. obj16.foo2; + ~~~~ +!!! error TS2339: Property 'foo2' does not exist on type 'H'. + } + + var obj17: any; + if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object' + obj17.foo1; + obj17.foo2; + } + + var obj18: any; + if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function' + obj18.foo1; + obj18.foo2; } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js index 7e6b3324470..40ef6587e75 100644 --- a/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js +++ b/tests/baselines/reference/typeGuardsWithInstanceOfByConstructorSignature.js @@ -14,7 +14,7 @@ if (obj1 instanceof A) { // narrowed to A. } var obj2: any; -if (obj2 instanceof A) { // can't narrow type from 'any' +if (obj2 instanceof A) { obj2.foo; obj2.bar; } @@ -36,7 +36,7 @@ if (obj3 instanceof B) { // narrowed to B. } var obj4: any; -if (obj4 instanceof B) { // can't narrow type from 'any' +if (obj4 instanceof B) { obj4.foo = "str"; obj4.foo = 1; obj4.bar = "str"; @@ -68,7 +68,7 @@ if (obj5 instanceof C) { // narrowed to C1|C2. } var obj6: any; -if (obj6 instanceof C) { // can't narrow type from 'any' +if (obj6 instanceof C) { obj6.foo; obj6.bar1; obj6.bar2; @@ -87,7 +87,7 @@ if (obj7 instanceof D) { // narrowed to D. } var obj8: any; -if (obj8 instanceof D) { // can't narrow type from 'any' +if (obj8 instanceof D) { obj8.foo; obj8.bar; } @@ -114,7 +114,7 @@ if (obj9 instanceof E) { // narrowed to E1 | E2 } var obj10: any; -if (obj10 instanceof E) { // can't narrow type from 'any' +if (obj10 instanceof E) { obj10.foo; obj10.bar1; obj10.bar2; @@ -137,7 +137,7 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a } var obj12: any; -if (obj12 instanceof F) { // can't narrow type from 'any' +if (obj12 instanceof F) { obj12.foo; obj12.bar; } @@ -162,7 +162,7 @@ if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype prop } var obj14: any; -if (obj14 instanceof G) { // can't narrow type from 'any' +if (obj14 instanceof G) { obj14.foo1; obj14.foo2; } @@ -184,10 +184,22 @@ if (obj15 instanceof H) { // narrowed to H. } var obj16: any; -if (obj16 instanceof H) { // can't narrow type from 'any' +if (obj16 instanceof H) { obj16.foo1; obj16.foo2; } + +var obj17: any; +if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object' + obj17.foo1; + obj17.foo2; +} + +var obj18: any; +if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function' + obj18.foo1; + obj18.foo2; +} //// [typeGuardsWithInstanceOfByConstructorSignature.js] @@ -278,3 +290,13 @@ if (obj16 instanceof H) { obj16.foo1; obj16.foo2; } +var obj17; +if (obj17 instanceof Object) { + obj17.foo1; + obj17.foo2; +} +var obj18; +if (obj18 instanceof Function) { + obj18.foo1; + obj18.foo2; +} diff --git a/tests/baselines/reference/typeReferenceDirectives1.js b/tests/baselines/reference/typeReferenceDirectives1.js index 775af9c5283..a6210819931 100644 --- a/tests/baselines/reference/typeReferenceDirectives1.js +++ b/tests/baselines/reference/typeReferenceDirectives1.js @@ -2,7 +2,6 @@ //// [index.d.ts] - interface $ { x } //// [app.ts] diff --git a/tests/baselines/reference/typeReferenceDirectives1.symbols b/tests/baselines/reference/typeReferenceDirectives1.symbols index 55c17b219ec..a33a2aba408 100644 --- a/tests/baselines/reference/typeReferenceDirectives1.symbols +++ b/tests/baselines/reference/typeReferenceDirectives1.symbols @@ -9,8 +9,7 @@ interface A { } === /types/lib/index.d.ts === - interface $ { x } >$ : Symbol($, Decl(index.d.ts, 0, 0)) ->x : Symbol($.x, Decl(index.d.ts, 2, 13)) +>x : Symbol($.x, Decl(index.d.ts, 1, 13)) diff --git a/tests/baselines/reference/typeReferenceDirectives1.types b/tests/baselines/reference/typeReferenceDirectives1.types index 05080e05651..be789a08ddc 100644 --- a/tests/baselines/reference/typeReferenceDirectives1.types +++ b/tests/baselines/reference/typeReferenceDirectives1.types @@ -9,7 +9,6 @@ interface A { } === /types/lib/index.d.ts === - interface $ { x } >$ : $ >x : any diff --git a/tests/baselines/reference/typingsLookup2.js b/tests/baselines/reference/typingsLookup2.js new file mode 100644 index 00000000000..3e816526af2 --- /dev/null +++ b/tests/baselines/reference/typingsLookup2.js @@ -0,0 +1,9 @@ +//// [tests/cases/conformance/typings/typingsLookup2.ts] //// + +//// [package.json] +{ "typings": null } + +//// [a.ts] + + +//// [a.js] diff --git a/tests/baselines/reference/typingsLookup2.symbols b/tests/baselines/reference/typingsLookup2.symbols new file mode 100644 index 00000000000..7223c8589a6 --- /dev/null +++ b/tests/baselines/reference/typingsLookup2.symbols @@ -0,0 +1,3 @@ +=== /a.ts === + +No type information for this code. \ No newline at end of file diff --git a/tests/baselines/reference/typingsLookup2.trace.json b/tests/baselines/reference/typingsLookup2.trace.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/tests/baselines/reference/typingsLookup2.trace.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/tests/baselines/reference/typingsLookup2.types b/tests/baselines/reference/typingsLookup2.types new file mode 100644 index 00000000000..7223c8589a6 --- /dev/null +++ b/tests/baselines/reference/typingsLookup2.types @@ -0,0 +1,3 @@ +=== /a.ts === + +No type information for this code. \ No newline at end of file diff --git a/tests/baselines/reference/typingsLookup3.js b/tests/baselines/reference/typingsLookup3.js new file mode 100644 index 00000000000..b3be036b4ae --- /dev/null +++ b/tests/baselines/reference/typingsLookup3.js @@ -0,0 +1,13 @@ +//// [tests/cases/conformance/typings/typingsLookup3.ts] //// + +//// [index.d.ts] +declare var $: { x: any }; + +//// [a.ts] +/// +$.x; + + +//// [a.js] +/// +$.x; diff --git a/tests/baselines/reference/typingsLookup3.symbols b/tests/baselines/reference/typingsLookup3.symbols new file mode 100644 index 00000000000..e641afb183b --- /dev/null +++ b/tests/baselines/reference/typingsLookup3.symbols @@ -0,0 +1,12 @@ +=== /a.ts === +/// +$.x; +>$.x : Symbol(x, Decl(index.d.ts, 0, 16)) +>$ : Symbol($, Decl(index.d.ts, 0, 11)) +>x : Symbol(x, Decl(index.d.ts, 0, 16)) + +=== /node_modules/@types/jquery/index.d.ts === +declare var $: { x: any }; +>$ : Symbol($, Decl(index.d.ts, 0, 11)) +>x : Symbol(x, Decl(index.d.ts, 0, 16)) + diff --git a/tests/baselines/reference/typingsLookup3.trace.json b/tests/baselines/reference/typingsLookup3.trace.json new file mode 100644 index 00000000000..83b0e91d6c7 --- /dev/null +++ b/tests/baselines/reference/typingsLookup3.trace.json @@ -0,0 +1,12 @@ +[ + "======== Resolving type reference directive 'jquery', containing file '/a.ts', root directory '/node_modules/@types'. ========", + "Resolving with primary search path '/node_modules/@types'", + "File '/node_modules/@types/jquery/package.json' does not exist.", + "File '/node_modules/@types/jquery/index.d.ts' exist - use it as a name resolution result.", + "======== Type reference directive 'jquery' was successfully resolved to '/node_modules/@types/jquery/index.d.ts', primary: true. ========", + "======== Resolving type reference directive 'jquery', containing file '/__inferred type names__.ts', root directory '/node_modules/@types'. ========", + "Resolving with primary search path '/node_modules/@types'", + "File '/node_modules/@types/jquery/package.json' does not exist.", + "File '/node_modules/@types/jquery/index.d.ts' exist - use it as a name resolution result.", + "======== Type reference directive 'jquery' was successfully resolved to '/node_modules/@types/jquery/index.d.ts', primary: true. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/typingsLookup3.types b/tests/baselines/reference/typingsLookup3.types new file mode 100644 index 00000000000..f57a974077e --- /dev/null +++ b/tests/baselines/reference/typingsLookup3.types @@ -0,0 +1,12 @@ +=== /a.ts === +/// +$.x; +>$.x : any +>$ : { x: any; } +>x : any + +=== /node_modules/@types/jquery/index.d.ts === +declare var $: { x: any }; +>$ : { x: any; } +>x : any + diff --git a/tests/baselines/reference/umd-augmentation-1.symbols b/tests/baselines/reference/umd-augmentation-1.symbols index 645511350a9..70715649894 100644 --- a/tests/baselines/reference/umd-augmentation-1.symbols +++ b/tests/baselines/reference/umd-augmentation-1.symbols @@ -39,6 +39,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; +>Math2d : Symbol(Math2d, Decl(index.d.ts, 0, 0)) export interface Point { >Point : Symbol(Point, Decl(index.d.ts, 1, 27)) diff --git a/tests/baselines/reference/umd-augmentation-1.types b/tests/baselines/reference/umd-augmentation-1.types index 31ac43fe855..59b577141e0 100644 --- a/tests/baselines/reference/umd-augmentation-1.types +++ b/tests/baselines/reference/umd-augmentation-1.types @@ -48,7 +48,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; ->Math2d : any +>Math2d : typeof Math2d export interface Point { >Point : Point diff --git a/tests/baselines/reference/umd-augmentation-2.symbols b/tests/baselines/reference/umd-augmentation-2.symbols index bd6584d3d74..a31ed61d531 100644 --- a/tests/baselines/reference/umd-augmentation-2.symbols +++ b/tests/baselines/reference/umd-augmentation-2.symbols @@ -37,6 +37,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; +>Math2d : Symbol(Math2d, Decl(index.d.ts, 0, 0)) export interface Point { >Point : Symbol(Point, Decl(index.d.ts, 1, 27)) diff --git a/tests/baselines/reference/umd-augmentation-2.types b/tests/baselines/reference/umd-augmentation-2.types index 20bba091903..24fe6df3c3c 100644 --- a/tests/baselines/reference/umd-augmentation-2.types +++ b/tests/baselines/reference/umd-augmentation-2.types @@ -46,7 +46,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; ->Math2d : any +>Math2d : typeof Math2d export interface Point { >Point : Point diff --git a/tests/baselines/reference/umd-augmentation-3.symbols b/tests/baselines/reference/umd-augmentation-3.symbols index 3193e83cd19..3e2df7c8a89 100644 --- a/tests/baselines/reference/umd-augmentation-3.symbols +++ b/tests/baselines/reference/umd-augmentation-3.symbols @@ -39,6 +39,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; +>Math2d : Symbol(Math2d, Decl(index.d.ts, 0, 0)) export = M2D; >M2D : Symbol(M2D, Decl(index.d.ts, 3, 13)) diff --git a/tests/baselines/reference/umd-augmentation-3.types b/tests/baselines/reference/umd-augmentation-3.types index 854ebceff37..39bc28cbd24 100644 --- a/tests/baselines/reference/umd-augmentation-3.types +++ b/tests/baselines/reference/umd-augmentation-3.types @@ -48,7 +48,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; ->Math2d : any +>Math2d : typeof Math2d export = M2D; >M2D : typeof M2D diff --git a/tests/baselines/reference/umd-augmentation-4.symbols b/tests/baselines/reference/umd-augmentation-4.symbols index 3f2cc913d86..7be5d278bf2 100644 --- a/tests/baselines/reference/umd-augmentation-4.symbols +++ b/tests/baselines/reference/umd-augmentation-4.symbols @@ -37,6 +37,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; +>Math2d : Symbol(Math2d, Decl(index.d.ts, 0, 0)) export = M2D; >M2D : Symbol(M2D, Decl(index.d.ts, 3, 13)) diff --git a/tests/baselines/reference/umd-augmentation-4.types b/tests/baselines/reference/umd-augmentation-4.types index 71783d03012..246270aa5a6 100644 --- a/tests/baselines/reference/umd-augmentation-4.types +++ b/tests/baselines/reference/umd-augmentation-4.types @@ -46,7 +46,7 @@ var t = p.x; === tests/cases/conformance/externalModules/node_modules/math2d/index.d.ts === export as namespace Math2d; ->Math2d : any +>Math2d : typeof Math2d export = M2D; >M2D : typeof M2D diff --git a/tests/baselines/reference/umd1.symbols b/tests/baselines/reference/umd1.symbols index 9b964456bcf..0307a9b78d3 100644 --- a/tests/baselines/reference/umd1.symbols +++ b/tests/baselines/reference/umd1.symbols @@ -30,4 +30,5 @@ export interface Thing { n: typeof x } >x : Symbol(x, Decl(foo.d.ts, 1, 10)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 3, 38)) diff --git a/tests/baselines/reference/umd1.types b/tests/baselines/reference/umd1.types index 1767f3b5a89..1379bba8d5d 100644 --- a/tests/baselines/reference/umd1.types +++ b/tests/baselines/reference/umd1.types @@ -31,5 +31,5 @@ export interface Thing { n: typeof x } >x : number export as namespace Foo; ->Foo : any +>Foo : typeof Foo diff --git a/tests/baselines/reference/umd3.symbols b/tests/baselines/reference/umd3.symbols index 165fd81597a..c1fd3d6d74f 100644 --- a/tests/baselines/reference/umd3.symbols +++ b/tests/baselines/reference/umd3.symbols @@ -32,4 +32,5 @@ export interface Thing { n: typeof x } >x : Symbol(x, Decl(foo.d.ts, 1, 10)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 3, 38)) diff --git a/tests/baselines/reference/umd3.types b/tests/baselines/reference/umd3.types index 85ee6bafe5e..23149e58d76 100644 --- a/tests/baselines/reference/umd3.types +++ b/tests/baselines/reference/umd3.types @@ -33,5 +33,5 @@ export interface Thing { n: typeof x } >x : number export as namespace Foo; ->Foo : any +>Foo : typeof Foo diff --git a/tests/baselines/reference/umd4.symbols b/tests/baselines/reference/umd4.symbols index 8403187198b..d266997770b 100644 --- a/tests/baselines/reference/umd4.symbols +++ b/tests/baselines/reference/umd4.symbols @@ -32,4 +32,5 @@ export interface Thing { n: typeof x } >x : Symbol(x, Decl(foo.d.ts, 1, 10)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 3, 38)) diff --git a/tests/baselines/reference/umd4.types b/tests/baselines/reference/umd4.types index 579599f5661..18794258517 100644 --- a/tests/baselines/reference/umd4.types +++ b/tests/baselines/reference/umd4.types @@ -33,5 +33,5 @@ export interface Thing { n: typeof x } >x : number export as namespace Foo; ->Foo : any +>Foo : typeof Foo diff --git a/tests/baselines/reference/umd6.symbols b/tests/baselines/reference/umd6.symbols index d08507f1ea2..958875b79eb 100644 --- a/tests/baselines/reference/umd6.symbols +++ b/tests/baselines/reference/umd6.symbols @@ -18,4 +18,5 @@ export = Thing; >Thing : Symbol(Thing, Decl(foo.d.ts, 0, 0)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 4, 15)) diff --git a/tests/baselines/reference/umd6.types b/tests/baselines/reference/umd6.types index 7318a43057a..5b1469cbd6c 100644 --- a/tests/baselines/reference/umd6.types +++ b/tests/baselines/reference/umd6.types @@ -19,5 +19,5 @@ export = Thing; >Thing : typeof Thing export as namespace Foo; ->Foo : any +>Foo : typeof Thing diff --git a/tests/baselines/reference/umd7.symbols b/tests/baselines/reference/umd7.symbols index 0b3ef17fb7b..19b56ed30f3 100644 --- a/tests/baselines/reference/umd7.symbols +++ b/tests/baselines/reference/umd7.symbols @@ -13,4 +13,5 @@ export = Thing; >Thing : Symbol(Thing, Decl(foo.d.ts, 0, 0)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 2, 15)) diff --git a/tests/baselines/reference/umd7.types b/tests/baselines/reference/umd7.types index 60782543710..d83e039b674 100644 --- a/tests/baselines/reference/umd7.types +++ b/tests/baselines/reference/umd7.types @@ -14,5 +14,5 @@ export = Thing; >Thing : () => number export as namespace Foo; ->Foo : any +>Foo : () => number diff --git a/tests/baselines/reference/umd8.symbols b/tests/baselines/reference/umd8.symbols index 8c38f267a2a..97da693b38f 100644 --- a/tests/baselines/reference/umd8.symbols +++ b/tests/baselines/reference/umd8.symbols @@ -22,4 +22,5 @@ export = Thing; >Thing : Symbol(Thing, Decl(foo.d.ts, 0, 0)) export as namespace Foo; +>Foo : Symbol(Foo, Decl(foo.d.ts, 4, 15)) diff --git a/tests/baselines/reference/umd8.types b/tests/baselines/reference/umd8.types index 0e66a49b963..ae3bdfd5fc8 100644 --- a/tests/baselines/reference/umd8.types +++ b/tests/baselines/reference/umd8.types @@ -23,5 +23,5 @@ export = Thing; >Thing : Thing export as namespace Foo; ->Foo : any +>Foo : typeof Thing diff --git a/tests/baselines/reference/unusedParameterProperty1.errors.txt b/tests/baselines/reference/unusedParameterProperty1.errors.txt new file mode 100644 index 00000000000..0581199313f --- /dev/null +++ b/tests/baselines/reference/unusedParameterProperty1.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/unusedParameterProperty1.ts(3,25): error TS6138: Property 'used' is declared but never used. + + +==== tests/cases/compiler/unusedParameterProperty1.ts (1 errors) ==== + + class A { + constructor(private used: string) { + ~~~~ +!!! error TS6138: Property 'used' is declared but never used. + let foge = used; + foge += ""; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedParameterProperty1.js b/tests/baselines/reference/unusedParameterProperty1.js new file mode 100644 index 00000000000..d0b608a6d87 --- /dev/null +++ b/tests/baselines/reference/unusedParameterProperty1.js @@ -0,0 +1,19 @@ +//// [unusedParameterProperty1.ts] + +class A { + constructor(private used: string) { + let foge = used; + foge += ""; + } +} + + +//// [unusedParameterProperty1.js] +var A = (function () { + function A(used) { + this.used = used; + var foge = used; + foge += ""; + } + return A; +}()); diff --git a/tests/baselines/reference/unusedParameterProperty2.errors.txt b/tests/baselines/reference/unusedParameterProperty2.errors.txt new file mode 100644 index 00000000000..cb3b3e95556 --- /dev/null +++ b/tests/baselines/reference/unusedParameterProperty2.errors.txt @@ -0,0 +1,14 @@ +tests/cases/compiler/unusedParameterProperty2.ts(3,25): error TS6138: Property 'used' is declared but never used. + + +==== tests/cases/compiler/unusedParameterProperty2.ts (1 errors) ==== + + class A { + constructor(private used) { + ~~~~ +!!! error TS6138: Property 'used' is declared but never used. + let foge = used; + foge += ""; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/unusedParameterProperty2.js b/tests/baselines/reference/unusedParameterProperty2.js new file mode 100644 index 00000000000..2bb04fde088 --- /dev/null +++ b/tests/baselines/reference/unusedParameterProperty2.js @@ -0,0 +1,19 @@ +//// [unusedParameterProperty2.ts] + +class A { + constructor(private used) { + let foge = used; + foge += ""; + } +} + + +//// [unusedParameterProperty2.js] +var A = (function () { + function A(used) { + this.used = used; + var foge = used; + foge += ""; + } + return A; +}()); diff --git a/tests/cases/compiler/APISample_watcher.ts b/tests/cases/compiler/APISample_watcher.ts index 34baa04c850..07922bd35c7 100644 --- a/tests/cases/compiler/APISample_watcher.ts +++ b/tests/cases/compiler/APISample_watcher.ts @@ -23,7 +23,7 @@ declare var path: any; import * as ts from "typescript"; function watch(rootFileNames: string[], options: ts.CompilerOptions) { - const files: ts.Map<{ version: number }> = {}; + const files: ts.MapLike<{ version: number }> = {}; // initialize the list of files rootFileNames.forEach(fileName => { diff --git a/tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts b/tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts new file mode 100644 index 00000000000..d621cf460bc --- /dev/null +++ b/tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts @@ -0,0 +1,18 @@ +class A { a } +class B extends A { b } +class C extends Array { c } +declare var ara: A[]; +declare var arb: B[]; +declare var cra: C
; +declare var crb: C; +declare var rra: ReadonlyArray; +declare var rrb: ReadonlyArray; +rra = ara; +rrb = arb; // OK, Array is assignable to ReadonlyArray +rra = arb; +rrb = ara; // error: 'A' is not assignable to 'B' + +rra = cra; +rra = crb; // OK, C is assignable to ReadonlyArray +rrb = crb; +rrb = cra; // error: 'A' is not assignable to 'B' diff --git a/tests/cases/compiler/controlFlowInstanceof.ts b/tests/cases/compiler/controlFlowInstanceof.ts new file mode 100644 index 00000000000..56f3ff97e4c --- /dev/null +++ b/tests/cases/compiler/controlFlowInstanceof.ts @@ -0,0 +1,99 @@ +// @target: es6 + +// Repros from #10167 + +function f1(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + s; // Set + s.add(42); +} + +function f2(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Promise) { + s; // Set & Promise + } + s; // Set + s.add(42); +} + +function f3(s: Set | Set) { + s; // Set | Set + if (s instanceof Set) { + s; // Set | Set + } + else { + s; // never + } +} + +function f4(s: Set | Set) { + s = new Set(); + s; // Set + if (s instanceof Set) { + s; // Set + } + else { + s; // never + } +} + +// More tests + +class A { a: string } +class B extends A { b: string } +class C extends A { c: string } + +function foo(x: A | undefined) { + x; // A | undefined + if (x instanceof B || x instanceof C) { + x; // B | C + } + x; // A | undefined + if (x instanceof B && x instanceof C) { + x; // B & C + } + x; // A | undefined + if (!x) { + return; + } + x; // A + if (x instanceof B) { + x; // B + if (x instanceof C) { + x; // B & C + } + else { + x; // B + } + x; // B + } + else { + x; // A + } + x; // A +} + +// X is neither assignable to Y nor a subtype of Y +// Y is assignable to X, but not a subtype of X + +interface X { + x?: string; +} + +class Y { + y: string; +} + +function goo(x: X) { + x; + if (x instanceof Y) { + x.y; + } + x; +} \ No newline at end of file diff --git a/tests/cases/compiler/discriminantPropertyCheck.ts b/tests/cases/compiler/discriminantPropertyCheck.ts new file mode 100644 index 00000000000..e493f6bf770 --- /dev/null +++ b/tests/cases/compiler/discriminantPropertyCheck.ts @@ -0,0 +1,69 @@ +// @strictNullChecks: true + +type Item = Item1 | Item2; + +interface Base { + bar: boolean; +} + +interface Item1 extends Base { + kind: "A"; + foo: string | undefined; + baz: boolean; + qux: true; +} + +interface Item2 extends Base { + kind: "B"; + foo: string | undefined; + baz: boolean; + qux: false; +} + +function goo1(x: Item) { + if (x.kind === "A" && x.foo !== undefined) { + x.foo.length; + } +} + +function goo2(x: Item) { + if (x.foo !== undefined && x.kind === "A") { + x.foo.length; // Error, intervening discriminant guard + } +} + +function foo1(x: Item) { + if (x.bar && x.foo !== undefined) { + x.foo.length; + } +} + +function foo2(x: Item) { + if (x.foo !== undefined && x.bar) { + x.foo.length; + } +} + +function foo3(x: Item) { + if (x.baz && x.foo !== undefined) { + x.foo.length; + } +} + +function foo4(x: Item) { + if (x.foo !== undefined && x.baz) { + x.foo.length; + } +} + +function foo5(x: Item) { + if (x.qux && x.foo !== undefined) { + x.foo.length; + } +} + +function foo6(x: Item) { + if (x.foo !== undefined && x.qux) { + x.foo.length; // Error, intervening discriminant guard + } +} \ No newline at end of file diff --git a/tests/cases/compiler/discriminantsAndNullOrUndefined.ts b/tests/cases/compiler/discriminantsAndNullOrUndefined.ts new file mode 100644 index 00000000000..8346fa8adea --- /dev/null +++ b/tests/cases/compiler/discriminantsAndNullOrUndefined.ts @@ -0,0 +1,25 @@ +// @strictNullChecks: true + +// Repro from #10228 + +interface A { kind: 'A'; } +interface B { kind: 'B'; } + +type C = A | B | undefined; + +function never(_: never): never { + throw new Error(); +} + +function useA(_: A): void { } +function useB(_: B): void { } + +declare var c: C; + +if (c !== undefined) { + switch (c.kind) { + case 'A': useA(c); break; + case 'B': useB(c); break; + default: never(c); + } +} \ No newline at end of file diff --git a/tests/cases/compiler/discriminantsAndPrimitives.ts b/tests/cases/compiler/discriminantsAndPrimitives.ts new file mode 100644 index 00000000000..6352d741808 --- /dev/null +++ b/tests/cases/compiler/discriminantsAndPrimitives.ts @@ -0,0 +1,49 @@ +// @strictNullChecks: true + +// Repro from #10257 plus other tests + +interface Foo { + kind: "foo"; + name: string; +} + +interface Bar { + kind: "bar"; + length: string; +} + +function f1(x: Foo | Bar | string) { + if (typeof x !== 'string') { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f2(x: Foo | Bar | string | undefined) { + if (typeof x === "object") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f3(x: Foo | Bar | string | null) { + if (x && typeof x !== "string") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} + +function f4(x: Foo | Bar | string | number | null) { + if (x && typeof x === "object") { + switch(x.kind) { + case 'foo': + x.name; + } + } +} \ No newline at end of file diff --git a/tests/cases/compiler/discriminantsAndTypePredicates.ts b/tests/cases/compiler/discriminantsAndTypePredicates.ts new file mode 100644 index 00000000000..c21ab7ec8f4 --- /dev/null +++ b/tests/cases/compiler/discriminantsAndTypePredicates.ts @@ -0,0 +1,31 @@ +// Repro from #10145 + +interface A { type: 'A' } +interface B { type: 'B' } + +function isA(x: A | B): x is A { return x.type === 'A'; } +function isB(x: A | B): x is B { return x.type === 'B'; } + +function foo1(x: A | B): any { + x; // A | B + if (isA(x)) { + return x; // A + } + x; // B + if (isB(x)) { + return x; // B + } + x; // never +} + +function foo2(x: A | B): any { + x; // A | B + if (x.type === 'A') { + return x; // A + } + x; // B + if (x.type === 'B') { + return x; // B + } + x; // never +} \ No newline at end of file diff --git a/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts b/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts new file mode 100644 index 00000000000..e369cd9fb50 --- /dev/null +++ b/tests/cases/compiler/emitCapturingThisInTupleDestructuring1.ts @@ -0,0 +1,4 @@ +declare function wrapper(x: any); +wrapper((array: [any]) => { + [this.test, this.test1, this.test2] = array; // even though there is a compiler error, we should still emit lexical capture for "this" +}); \ No newline at end of file diff --git a/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts b/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts new file mode 100644 index 00000000000..2bb931c4f4f --- /dev/null +++ b/tests/cases/compiler/emitCapturingThisInTupleDestructuring2.ts @@ -0,0 +1,10 @@ +var array1: [number, number] = [1, 2]; + +class B { + test: number; + test1: any; + test2: any; + method() { + () => [this.test, this.test1, this.test2] = array1; // even though there is a compiler error, we should still emit lexical capture for "this" + } +} \ No newline at end of file diff --git a/tests/cases/compiler/exportDefaultProperty.ts b/tests/cases/compiler/exportDefaultProperty.ts new file mode 100644 index 00000000000..4a4b4139025 --- /dev/null +++ b/tests/cases/compiler/exportDefaultProperty.ts @@ -0,0 +1,39 @@ +// This test is just like exportEqualsProperty, but with `export default`. + +// @Filename: declarations.d.ts +declare namespace foo.bar { + export type X = number; + export const X: number; +} + +declare module "foobar" { + export default foo.bar; +} + +declare module "foobarx" { + export default foo.bar.X; +} + +// @Filename: a.ts +namespace A { + export class B { constructor(b: number) {} } + export namespace B { export const b: number = 0; } +} +export default A.B; + +// @Filename: b.ts +export default "foo".length; + +// @Filename: index.ts +/// +import fooBar from "foobar"; +import X = fooBar.X; +import X2 from "foobarx"; +const x: X = X; +const x2: X2 = X2; + +import B from "./a"; +const b: B = new B(B.b); + +import fooLength from "./b"; +fooLength + 1; diff --git a/tests/cases/compiler/exportEqualsProperty.ts b/tests/cases/compiler/exportEqualsProperty.ts new file mode 100644 index 00000000000..0d14815a5bd --- /dev/null +++ b/tests/cases/compiler/exportEqualsProperty.ts @@ -0,0 +1,38 @@ +// This test is just like exportDefaultProperty, but with `export =`. + +// @Filename: declarations.d.ts +declare namespace foo.bar { + export type X = number; + export const X: number; +} + +declare module "foobar" { + export = foo.bar; +} + +declare module "foobarx" { + export = foo.bar.X; +} + +// @Filename: a.ts +namespace A { + export class B { constructor(b: number) {} } + export namespace B { export const b: number = 0; } +} +export = A.B; + +// @Filename: b.ts +export = "foo".length; + +// @Filename: index.ts +/// +import { X } from "foobar"; +import X2 = require("foobarx"); +const x: X = X; +const x2: X2 = X2; + +import B = require("./a"); +const b: B = new B(B.b); + +import fooLength = require("./b"); +fooLength + 1; diff --git a/tests/cases/compiler/exportToString.ts b/tests/cases/compiler/exportToString.ts new file mode 100644 index 00000000000..248df036bfd --- /dev/null +++ b/tests/cases/compiler/exportToString.ts @@ -0,0 +1,2 @@ +const toString = 0; +export { toString }; diff --git a/tests/cases/compiler/implicitConstParameters.ts b/tests/cases/compiler/implicitConstParameters.ts new file mode 100644 index 00000000000..97996789124 --- /dev/null +++ b/tests/cases/compiler/implicitConstParameters.ts @@ -0,0 +1,57 @@ +// @strictNullChecks: true + +function doSomething(cb: () => void) { + cb(); +} + +function fn(x: number | string) { + if (typeof x === 'number') { + doSomething(() => x.toFixed()); + } +} + +function f1(x: string | undefined) { + if (!x) { + return; + } + doSomething(() => x.length); +} + +function f2(x: string | undefined) { + if (x) { + doSomething(() => { + doSomething(() => x.length); + }); + } +} + +function f3(x: string | undefined) { + inner(); + function inner() { + if (x) { + doSomething(() => x.length); + } + } +} + +function f4(x: string | undefined) { + x = "abc"; // causes x to be considered non-const + if (x) { + doSomething(() => x.length); + } +} + +function f5(x: string | undefined) { + if (x) { + doSomething(() => x.length); + } + x = "abc"; // causes x to be considered non-const +} + + +function f6(x: string | undefined) { + const y = x || ""; + if (x) { + doSomething(() => y.length); + } +} \ No newline at end of file diff --git a/tests/cases/compiler/indexWithUndefinedAndNull.ts b/tests/cases/compiler/indexWithUndefinedAndNull.ts new file mode 100644 index 00000000000..2aeb2ee0b1d --- /dev/null +++ b/tests/cases/compiler/indexWithUndefinedAndNull.ts @@ -0,0 +1,13 @@ +// @strictNullChecks: false +interface N { + [n: number]: string; +} +interface S { + [s: string]: number; +} +let n: N; +let s: S; +let str: string = n[undefined]; +str = n[null]; +let num: number = s[undefined]; +num = s[null]; diff --git a/tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts b/tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts new file mode 100644 index 00000000000..f8fe0a323c6 --- /dev/null +++ b/tests/cases/compiler/indexWithUndefinedAndNullStrictNullChecks.ts @@ -0,0 +1,13 @@ +// @strictNullChecks: true +interface N { + [n: number]: string; +} +interface S { + [s: string]: number; +} +let n: N; +let s: S; +let str: string = n[undefined]; +str = n[null]; +let num: number = s[undefined]; +num = s[null]; diff --git a/tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts b/tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts new file mode 100644 index 00000000000..564f7a9c22e --- /dev/null +++ b/tests/cases/compiler/instanceofWithStructurallyIdenticalTypes.ts @@ -0,0 +1,69 @@ +// Repro from #7271 + +class C1 { item: string } +class C2 { item: string[] } +class C3 { item: string } + +function foo1(x: C1 | C2 | C3): string { + if (x instanceof C1) { + return x.item; + } + else if (x instanceof C2) { + return x.item[0]; + } + else if (x instanceof C3) { + return x.item; + } + return "error"; +} + +function isC1(c: C1 | C2 | C3): c is C1 { return c instanceof C1 } +function isC2(c: C1 | C2 | C3): c is C2 { return c instanceof C2 } +function isC3(c: C1 | C2 | C3): c is C3 { return c instanceof C3 } + +function foo2(x: C1 | C2 | C3): string { + if (isC1(x)) { + return x.item; + } + else if (isC2(x)) { + return x.item[0]; + } + else if (isC3(x)) { + return x.item; + } + return "error"; +} + +// More tests + +class A { a: string } +class A1 extends A { } +class A2 { a: string } +class B extends A { b: string } + +function goo(x: A) { + if (x instanceof A) { + x; // A + } + else { + x; // never + } + if (x instanceof A1) { + x; // A1 + } + else { + x; // A + } + if (x instanceof A2) { + x; // A2 + } + else { + x; // A + } + if (x instanceof B) { + x; // B + } + else { + x; // A + } +} diff --git a/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts b/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts new file mode 100644 index 00000000000..b6d0d338d85 --- /dev/null +++ b/tests/cases/compiler/thisInTupleTypeParameterConstraints.ts @@ -0,0 +1,22 @@ +/// + +interface Boolean {} +interface IArguments {} +interface Function {} +interface Number {} +interface RegExp {} +interface Object {} +interface String {} + +interface Array { + // 4 methods will run out of memory if this-types are not instantiated + // correctly for tuple types that are type parameter constraints + map(arg: this): void; + reduceRight(arg: this): void; + reduce(arg: this): void; + reduce2(arg: this): void; +} + +declare function f number]>(a: T): void; +let x: [(x: number) => number]; +f(x); diff --git a/tests/cases/compiler/typeReferenceDirectives1.ts b/tests/cases/compiler/typeReferenceDirectives1.ts index e17e498b978..13c84c7ae5e 100644 --- a/tests/cases/compiler/typeReferenceDirectives1.ts +++ b/tests/cases/compiler/typeReferenceDirectives1.ts @@ -2,7 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types - +// @currentDirectory: / // @filename: /types/lib/index.d.ts interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives10.ts b/tests/cases/compiler/typeReferenceDirectives10.ts index 61971ba44b2..1eb796d03fd 100644 --- a/tests/cases/compiler/typeReferenceDirectives10.ts +++ b/tests/cases/compiler/typeReferenceDirectives10.ts @@ -2,6 +2,7 @@ // @declaration: true // @typeRoots: /types // @traceResolution: true +// @currentDirectory: / // @filename: /ref.d.ts export interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives11.ts b/tests/cases/compiler/typeReferenceDirectives11.ts index 2d93e22bdcf..8b56459cd69 100644 --- a/tests/cases/compiler/typeReferenceDirectives11.ts +++ b/tests/cases/compiler/typeReferenceDirectives11.ts @@ -4,6 +4,7 @@ // @traceResolution: true // @types: lib // @out: output.js +// @currentDirectory: / // @filename: /types/lib/index.d.ts diff --git a/tests/cases/compiler/typeReferenceDirectives12.ts b/tests/cases/compiler/typeReferenceDirectives12.ts index efdb1e8312b..f1abe27f05f 100644 --- a/tests/cases/compiler/typeReferenceDirectives12.ts +++ b/tests/cases/compiler/typeReferenceDirectives12.ts @@ -3,6 +3,7 @@ // @typeRoots: /types // @traceResolution: true // @out: output.js +// @currentDirectory: / // @filename: /types/lib/index.d.ts diff --git a/tests/cases/compiler/typeReferenceDirectives13.ts b/tests/cases/compiler/typeReferenceDirectives13.ts index 124c31274ac..f9dede73267 100644 --- a/tests/cases/compiler/typeReferenceDirectives13.ts +++ b/tests/cases/compiler/typeReferenceDirectives13.ts @@ -2,6 +2,7 @@ // @declaration: true // @typeRoots: /types // @traceResolution: true +// @currentDirectory: / // @filename: /ref.d.ts export interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives2.ts b/tests/cases/compiler/typeReferenceDirectives2.ts index 31a01a0b8e4..44218683a5a 100644 --- a/tests/cases/compiler/typeReferenceDirectives2.ts +++ b/tests/cases/compiler/typeReferenceDirectives2.ts @@ -3,6 +3,7 @@ // @declaration: true // @typeRoots: /types // @types: lib +// @currentDirectory: / // @filename: /types/lib/index.d.ts interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives3.ts b/tests/cases/compiler/typeReferenceDirectives3.ts index 4c2729ab389..1baf0bdac9d 100644 --- a/tests/cases/compiler/typeReferenceDirectives3.ts +++ b/tests/cases/compiler/typeReferenceDirectives3.ts @@ -2,6 +2,7 @@ // @declaration: true // @typeRoots: /types // @traceResolution: true +// @currentDirectory: / // $ comes from d.ts file - no need to add type reference directive diff --git a/tests/cases/compiler/typeReferenceDirectives4.ts b/tests/cases/compiler/typeReferenceDirectives4.ts index ac7346895ef..dfa87b65138 100644 --- a/tests/cases/compiler/typeReferenceDirectives4.ts +++ b/tests/cases/compiler/typeReferenceDirectives4.ts @@ -2,6 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types +// @currentDirectory: / // $ comes from d.ts file - no need to add type reference directive diff --git a/tests/cases/compiler/typeReferenceDirectives5.ts b/tests/cases/compiler/typeReferenceDirectives5.ts index bb24b82b324..e81ae663e24 100644 --- a/tests/cases/compiler/typeReferenceDirectives5.ts +++ b/tests/cases/compiler/typeReferenceDirectives5.ts @@ -2,6 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types +// @currentDirectory: / // @filename: /ref.d.ts export interface $ { x } diff --git a/tests/cases/compiler/typeReferenceDirectives6.ts b/tests/cases/compiler/typeReferenceDirectives6.ts index 7154963f1ef..edf2ece7e06 100644 --- a/tests/cases/compiler/typeReferenceDirectives6.ts +++ b/tests/cases/compiler/typeReferenceDirectives6.ts @@ -2,6 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types +// @currentDirectory: / // $ comes from type declaration file - type reference directive should be added diff --git a/tests/cases/compiler/typeReferenceDirectives7.ts b/tests/cases/compiler/typeReferenceDirectives7.ts index 79d42fa7018..e9335b07082 100644 --- a/tests/cases/compiler/typeReferenceDirectives7.ts +++ b/tests/cases/compiler/typeReferenceDirectives7.ts @@ -2,6 +2,7 @@ // @traceResolution: true // @declaration: true // @typeRoots: /types +// @currentDirectory: / // local value shadows global - no need to add type reference directive diff --git a/tests/cases/compiler/typeReferenceDirectives8.ts b/tests/cases/compiler/typeReferenceDirectives8.ts index c7725a3aab1..bed69cbf357 100644 --- a/tests/cases/compiler/typeReferenceDirectives8.ts +++ b/tests/cases/compiler/typeReferenceDirectives8.ts @@ -3,6 +3,7 @@ // @typeRoots: /types // @traceResolution: true // @types: lib +// @currentDirectory: / // @filename: /types/lib/index.d.ts diff --git a/tests/cases/compiler/typeReferenceDirectives9.ts b/tests/cases/compiler/typeReferenceDirectives9.ts index 610c7173c89..1ad1aa52288 100644 --- a/tests/cases/compiler/typeReferenceDirectives9.ts +++ b/tests/cases/compiler/typeReferenceDirectives9.ts @@ -2,6 +2,7 @@ // @declaration: true // @typeRoots: /types // @traceResolution: true +// @currentDirectory: / // @filename: /types/lib/index.d.ts diff --git a/tests/cases/compiler/unusedParameterProperty1.ts b/tests/cases/compiler/unusedParameterProperty1.ts new file mode 100644 index 00000000000..61c4374c60a --- /dev/null +++ b/tests/cases/compiler/unusedParameterProperty1.ts @@ -0,0 +1,9 @@ +//@noUnusedLocals:true +//@noUnusedParameters:true + +class A { + constructor(private used: string) { + let foge = used; + foge += ""; + } +} diff --git a/tests/cases/compiler/unusedParameterProperty2.ts b/tests/cases/compiler/unusedParameterProperty2.ts new file mode 100644 index 00000000000..b9e05fbc967 --- /dev/null +++ b/tests/cases/compiler/unusedParameterProperty2.ts @@ -0,0 +1,9 @@ +//@noUnusedLocals:true +//@noUnusedParameters:true + +class A { + constructor(private used) { + let foge = used; + foge += ""; + } +} diff --git a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts index c9e9be92f8e..dc1cf97fe59 100644 --- a/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts +++ b/tests/cases/conformance/controlFlow/controlFlowIfStatement.ts @@ -34,3 +34,19 @@ function b() { } x; // string } +function c(data: string | T): T { + if (typeof data === 'string') { + return JSON.parse(data); + } + else { + return data; + } +} +function d(data: string | T): never { + if (typeof data === 'string') { + throw new Error('will always happen'); + } + else { + return data; + } +} diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts index b81dd26652b..0817954c35c 100644 --- a/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOfByConstructorSignature.ts @@ -13,7 +13,7 @@ if (obj1 instanceof A) { // narrowed to A. } var obj2: any; -if (obj2 instanceof A) { // can't narrow type from 'any' +if (obj2 instanceof A) { obj2.foo; obj2.bar; } @@ -35,7 +35,7 @@ if (obj3 instanceof B) { // narrowed to B. } var obj4: any; -if (obj4 instanceof B) { // can't narrow type from 'any' +if (obj4 instanceof B) { obj4.foo = "str"; obj4.foo = 1; obj4.bar = "str"; @@ -67,7 +67,7 @@ if (obj5 instanceof C) { // narrowed to C1|C2. } var obj6: any; -if (obj6 instanceof C) { // can't narrow type from 'any' +if (obj6 instanceof C) { obj6.foo; obj6.bar1; obj6.bar2; @@ -86,7 +86,7 @@ if (obj7 instanceof D) { // narrowed to D. } var obj8: any; -if (obj8 instanceof D) { // can't narrow type from 'any' +if (obj8 instanceof D) { obj8.foo; obj8.bar; } @@ -113,7 +113,7 @@ if (obj9 instanceof E) { // narrowed to E1 | E2 } var obj10: any; -if (obj10 instanceof E) { // can't narrow type from 'any' +if (obj10 instanceof E) { obj10.foo; obj10.bar1; obj10.bar2; @@ -136,7 +136,7 @@ if (obj11 instanceof F) { // can't type narrowing, construct signature returns a } var obj12: any; -if (obj12 instanceof F) { // can't narrow type from 'any' +if (obj12 instanceof F) { obj12.foo; obj12.bar; } @@ -161,7 +161,7 @@ if (obj13 instanceof G) { // narrowed to G1. G1 is return type of prototype prop } var obj14: any; -if (obj14 instanceof G) { // can't narrow type from 'any' +if (obj14 instanceof G) { obj14.foo1; obj14.foo2; } @@ -183,7 +183,19 @@ if (obj15 instanceof H) { // narrowed to H. } var obj16: any; -if (obj16 instanceof H) { // can't narrow type from 'any' +if (obj16 instanceof H) { obj16.foo1; obj16.foo2; } + +var obj17: any; +if (obj17 instanceof Object) { // can't narrow type from 'any' to 'Object' + obj17.foo1; + obj17.foo2; +} + +var obj18: any; +if (obj18 instanceof Function) { // can't narrow type from 'any' to 'Function' + obj18.foo1; + obj18.foo2; +} diff --git a/tests/cases/conformance/jsx/tsxAttributeResolution14.tsx b/tests/cases/conformance/jsx/tsxAttributeResolution14.tsx new file mode 100644 index 00000000000..1e4418e7fba --- /dev/null +++ b/tests/cases/conformance/jsx/tsxAttributeResolution14.tsx @@ -0,0 +1,32 @@ +//@jsx: preserve +//@module: amd + +//@filename: react.d.ts +declare module JSX { + interface Element { } + interface IntrinsicElements { + div: any; + } + interface ElementAttributesProperty { prop: any } +} + +//@filename: file.tsx + +interface IProps { + primaryText: string, + [propName: string]: string | number +} + +function VerticalNavMenuItem(prop: IProps) { + return
props.primaryText
+} + +function VerticalNav() { + return ( +
+ // error + // ok + // error +
+ ) +} \ No newline at end of file diff --git a/tests/cases/conformance/jsx/tsxAttributeResolution5.tsx b/tests/cases/conformance/jsx/tsxAttributeResolution5.tsx index dd16ade10e3..83fb7b32f56 100644 --- a/tests/cases/conformance/jsx/tsxAttributeResolution5.tsx +++ b/tests/cases/conformance/jsx/tsxAttributeResolution5.tsx @@ -29,4 +29,4 @@ function make3 (obj: T) { ; // Error, missing x -; // OK +; // Error, missing toString diff --git a/tests/cases/conformance/references/library-reference-13.ts b/tests/cases/conformance/references/library-reference-13.ts index 92b4b259ba4..419643d9d0d 100644 --- a/tests/cases/conformance/references/library-reference-13.ts +++ b/tests/cases/conformance/references/library-reference-13.ts @@ -1,5 +1,6 @@ // @noImplicitReferences: true // @traceResolution: true +// @currentDirectory: / // load type declarations from types section of tsconfig diff --git a/tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts b/tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts new file mode 100644 index 00000000000..dfa60a415f9 --- /dev/null +++ b/tests/cases/conformance/types/any/narrowExceptionVariableInCatchClause.ts @@ -0,0 +1,23 @@ +declare function isFooError(x: any): x is { type: 'foo'; dontPanic(); }; + +function tryCatch() { + try { + // do stuff... + } + catch (err) { // err is implicitly 'any' and cannot be annotated + + if (isFooError(err)) { + err.dontPanic(); // OK + err.doPanic(); // ERROR: Property 'doPanic' does not exist on type '{...}' + } + + else if (err instanceof Error) { + err.message; + err.massage; // ERROR: Property 'massage' does not exist on type 'Error' + } + + else { + throw err; + } + } +} diff --git a/tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts b/tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts new file mode 100644 index 00000000000..4fbfc46060a --- /dev/null +++ b/tests/cases/conformance/types/any/narrowFromAnyWithInstanceof.ts @@ -0,0 +1,23 @@ +declare var x: any; + +if (x instanceof Function) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} + +if (x instanceof Object) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); +} + +if (x instanceof Error) { // 'any' is narrowed to types other than 'Function'/'Object' + x.message; + x.mesage; +} + +if (x instanceof Date) { + x.getDate(); + x.getHuors(); +} diff --git a/tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts b/tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts new file mode 100644 index 00000000000..473bd349b5f --- /dev/null +++ b/tests/cases/conformance/types/any/narrowFromAnyWithTypePredicate.ts @@ -0,0 +1,34 @@ +declare var x: any; +declare function isFunction(x): x is Function; +declare function isObject(x): x is Object; +declare function isAnything(x): x is {}; +declare function isError(x): x is Error; +declare function isDate(x): x is Date; + + +if (isFunction(x)) { // 'any' is not narrowed when target type is 'Function' + x(); + x(1, 2, 3); + x("hello!"); + x.prop; +} + +if (isObject(x)) { // 'any' is not narrowed when target type is 'Object' + x.method(); + x(); +} + +if (isAnything(x)) { // 'any' is narrowed to types other than 'Function'/'Object' (including {}) + x.method(); + x(); +} + +if (isError(x)) { + x.message; + x.mesage; +} + +if (isDate(x)) { + x.getDate(); + x.getHuors(); +} diff --git a/tests/cases/conformance/typings/typingsLookup1.ts b/tests/cases/conformance/typings/typingsLookup1.ts index 555d4569af3..150deef992a 100644 --- a/tests/cases/conformance/typings/typingsLookup1.ts +++ b/tests/cases/conformance/typings/typingsLookup1.ts @@ -1,5 +1,6 @@ // @traceResolution: true // @noImplicitReferences: true +// @currentDirectory: / // @filename: /tsconfig.json { "files": "a.ts" } diff --git a/tests/cases/conformance/typings/typingsLookup2.ts b/tests/cases/conformance/typings/typingsLookup2.ts new file mode 100644 index 00000000000..90e1e463f0a --- /dev/null +++ b/tests/cases/conformance/typings/typingsLookup2.ts @@ -0,0 +1,13 @@ +// @traceResolution: true +// @noImplicitReferences: true +// @currentDirectory: / +// This tests that an @types package with `"typings": null` is not automatically included. +// (If it were, this test would break because there are no typings to be found.) + +// @filename: /tsconfig.json +{} + +// @filename: /node_modules/@types/angular2/package.json +{ "typings": null } + +// @filename: /a.ts diff --git a/tests/cases/conformance/typings/typingsLookup3.ts b/tests/cases/conformance/typings/typingsLookup3.ts new file mode 100644 index 00000000000..a0bb15476b9 --- /dev/null +++ b/tests/cases/conformance/typings/typingsLookup3.ts @@ -0,0 +1,14 @@ +// @traceResolution: true +// @noImplicitReferences: true +// @currentDirectory: / +// This tests that `types` references are automatically lowercased. + +// @filename: /tsconfig.json +{ "files": "a.ts" } + +// @filename: /node_modules/@types/jquery/index.d.ts +declare var $: { x: any }; + +// @filename: /a.ts +/// +$.x; diff --git a/tests/cases/fourslash/findAllRefsForUMDModuleAlias1.ts b/tests/cases/fourslash/findAllRefsForUMDModuleAlias1.ts new file mode 100644 index 00000000000..4177e154532 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsForUMDModuleAlias1.ts @@ -0,0 +1,13 @@ +/// + +// @Filename: 0.d.ts +//// export function doThing(): string; +//// export function doTheOtherThing(): void; + +//// export as namespace [|myLib|]; + +// @Filename: 1.ts +//// /// +//// [|myLib|].doThing(); + +verify.rangesReferenceEachOther(); diff --git a/tests/cases/fourslash/javaScriptClass1.ts b/tests/cases/fourslash/javaScriptClass1.ts new file mode 100644 index 00000000000..19f7a39b615 --- /dev/null +++ b/tests/cases/fourslash/javaScriptClass1.ts @@ -0,0 +1,31 @@ +/// + +// Classes have their shape inferred from assignments +// to properties of 'this' in the constructor + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// class Foo { +//// constructor() { +//// this.bar = 'world'; +//// this.thing = () => 0; +//// this.union = 'foo'; +//// this.union = 100; +//// } +//// } +//// var x = new Foo(); +//// x/**/ + + +goTo.marker(); +edit.insert('.'); +verify.completionListContains("bar", /*displayText*/ undefined, /*documentation*/ undefined, "property"); +verify.completionListContains("thing", /*displayText*/ undefined, /*documentation*/ undefined, "property"); +verify.completionListContains("union", /*displayText*/ undefined, /*documentation*/ undefined, "property"); + +edit.insert('bar.'); +verify.completionListContains("substr", /*displayText*/ undefined, /*documentation*/ undefined, "method"); +edit.backspace('bar.'.length); + +edit.insert('union.'); +verify.completionListContains("toString", /*displayText*/ undefined, /*documentation*/ undefined, "method"); \ No newline at end of file diff --git a/tests/cases/fourslash/javaScriptClass2.ts b/tests/cases/fourslash/javaScriptClass2.ts new file mode 100644 index 00000000000..d6daa320c5a --- /dev/null +++ b/tests/cases/fourslash/javaScriptClass2.ts @@ -0,0 +1,22 @@ +/// + +// In an inferred class, we can rename successfully + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// class Foo { +//// constructor() { +//// this.[|union|] = 'foo'; +//// this./*1*/[|union|] = 100; +//// } +//// method() { return this./*2*/[|union|]; } +//// } +//// var x = new Foo(); +//// x./*3*/[|union|]; + +goTo.marker('1'); +verify.renameLocations(/*findInStrings*/false, /*findInComments*/false); +goTo.marker('2'); +verify.renameLocations(/*findInStrings*/false, /*findInComments*/false); +goTo.marker('3'); +verify.renameLocations(/*findInStrings*/false, /*findInComments*/false); diff --git a/tests/cases/fourslash/javaScriptClass3.ts b/tests/cases/fourslash/javaScriptClass3.ts new file mode 100644 index 00000000000..47004d53b04 --- /dev/null +++ b/tests/cases/fourslash/javaScriptClass3.ts @@ -0,0 +1,24 @@ +/// + +// In an inferred class, we can to-to-def successfully + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// class Foo { +//// constructor() { +//// /*dst1*/this.alpha = 10; +//// /*dst2*/this.beta = 'gamma'; +//// } +//// method() { return this.alpha; } +//// } +//// var x = new Foo(); +//// x.alpha/*src1*/; +//// x.beta/*src2*/; + +goTo.marker('src1'); +goTo.definition(); +verify.caretAtMarker('dst1'); + +goTo.marker('src2'); +goTo.definition(); +verify.caretAtMarker('dst2'); diff --git a/tests/cases/fourslash/javaScriptClass4.ts b/tests/cases/fourslash/javaScriptClass4.ts new file mode 100644 index 00000000000..1148d5f320e --- /dev/null +++ b/tests/cases/fourslash/javaScriptClass4.ts @@ -0,0 +1,22 @@ +/// + +// Classes have their shape inferred from assignments +// to properties of 'this' in the constructor + +// @allowNonTsExtensions: true +// @Filename: Foo.js +//// class Foo { +//// constructor() { +//// /** +//// * @type {string} +//// */ +//// this.baz = null; +//// } +//// } +//// var x = new Foo(); +//// x/**/ + +goTo.marker(); +edit.insert('.baz.'); +verify.completionListContains("substr", /*displayText*/ undefined, /*documentation*/ undefined, "method"); + diff --git a/tests/cases/fourslash/localGetReferences.ts b/tests/cases/fourslash/localGetReferences.ts index ada4947acde..b05346a366a 100644 --- a/tests/cases/fourslash/localGetReferences.ts +++ b/tests/cases/fourslash/localGetReferences.ts @@ -205,12 +205,13 @@ const rangesByText = test.rangesByText(); for (const text in rangesByText) { const ranges = rangesByText[text]; if (text === "globalVar") { - function isShadow(r) { - return r.marker && r.marker.data && r.marker.data.shadow; - } verify.rangesReferenceEachOther(ranges.filter(isShadow)); verify.rangesReferenceEachOther(ranges.filter(r => !isShadow(r))); } else { verify.rangesReferenceEachOther(ranges); } } + +function isShadow(r) { + return r.marker && r.marker.data && r.marker.data.shadow; +} diff --git a/tests/cases/fourslash/quickInfoForUMDModuleAlias.ts b/tests/cases/fourslash/quickInfoForUMDModuleAlias.ts new file mode 100644 index 00000000000..085ea33b874 --- /dev/null +++ b/tests/cases/fourslash/quickInfoForUMDModuleAlias.ts @@ -0,0 +1,17 @@ +/// + +// @Filename: 0.d.ts +//// export function doThing(): string; +//// export function doTheOtherThing(): void; + +//// export as namespace /*0*/myLib; + +// @Filename: 1.ts +//// /// +//// /*1*/myLib.doThing(); + +goTo.marker("0"); +verify.quickInfoIs("export namespace myLib"); + +goTo.marker("1"); +verify.quickInfoIs("export namespace myLib"); diff --git a/tests/cases/fourslash/renameAlias.ts b/tests/cases/fourslash/renameAlias.ts index e3f57ac7b41..e0408af656f 100644 --- a/tests/cases/fourslash/renameAlias.ts +++ b/tests/cases/fourslash/renameAlias.ts @@ -4,8 +4,8 @@ ////import [|M|] = SomeModule; ////import C = [|M|].SomeClass; -let ranges = test.ranges() -for (let range of ranges) { - goTo.position(range.start); - verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +let ranges = test.ranges() +for (let range of ranges) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); } \ No newline at end of file diff --git a/tests/cases/fourslash/renameUMDModuleAlias1.ts b/tests/cases/fourslash/renameUMDModuleAlias1.ts new file mode 100644 index 00000000000..94aabcdbde5 --- /dev/null +++ b/tests/cases/fourslash/renameUMDModuleAlias1.ts @@ -0,0 +1,17 @@ +/// + +// @Filename: 0.d.ts +//// export function doThing(): string; +//// export function doTheOtherThing(): void; + +//// export as namespace [|myLib|]; + +// @Filename: 1.ts +//// /// +//// [|myLib|].doThing(); + +const ranges = test.ranges() +for (const range of ranges) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false); +} \ No newline at end of file diff --git a/tests/cases/fourslash/renameUMDModuleAlias2.ts b/tests/cases/fourslash/renameUMDModuleAlias2.ts new file mode 100644 index 00000000000..0daa4087b0d --- /dev/null +++ b/tests/cases/fourslash/renameUMDModuleAlias2.ts @@ -0,0 +1,14 @@ +/// + +// @Filename: 0.d.ts +//// export function doThing(): string; +//// export function doTheOtherThing(): void; + +//// export as namespace /**/[|myLib|]; + +// @Filename: 1.ts +//// /// +//// myLib.doThing(); + +goTo.marker(); +verify.renameInfoSucceeded("myLib"); \ No newline at end of file diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index ac5f046c1dd..4d2cb7d4672 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -117,19 +117,21 @@ function handleResolutionRequest(filePath: string, res: http.ServerResponse) { let resolvedPath = path.resolve(filePath, ""); resolvedPath = resolvedPath.substring(resolvedPath.indexOf("tests")); resolvedPath = switchToForwardSlashes(resolvedPath); - send("success", res, resolvedPath); - return; + send(ResponseCode.Success, res, resolvedPath); } -function send(result: "fail", res: http.ServerResponse, contents: string, contentType?: string): void; -function send(result: "success", res: http.ServerResponse, contents: string, contentType?: string): void; -function send(result: "unknown", res: http.ServerResponse, contents: string, contentType?: string): void; -function send(result: string, res: http.ServerResponse, contents: string, contentType?: string): void -function send(result: string, res: http.ServerResponse, contents: string, contentType = "binary"): void { - const responseCode = result === "success" ? 200 : result === "fail" ? 500 : result === "unknown" ? 404 : parseInt(result); +const enum ResponseCode { + Success = 200, + BadRequest = 400, + NotFound = 404, + MethodNotAllowed = 405, + PayloadTooLarge = 413, + Fail = 500 +} + +function send(responseCode: number, res: http.ServerResponse, contents: string, contentType = "binary"): void { res.writeHead(responseCode, { "Content-Type": contentType }); res.end(contents); - return; } // Reads the data from a post request and passes it to the given callback @@ -142,7 +144,7 @@ function processPost(req: http.ServerRequest, res: http.ServerResponse, callback queryData += data; if (queryData.length > 1e8) { queryData = ""; - send("413", res, undefined); + send(ResponseCode.PayloadTooLarge, res, undefined); console.log("ERROR: destroying connection"); req.connection.destroy(); } @@ -155,7 +157,7 @@ function processPost(req: http.ServerRequest, res: http.ServerResponse, callback } else { - send("405", res, undefined); + send(ResponseCode.MethodNotAllowed, res, undefined); } } @@ -201,16 +203,16 @@ function handleRequestOperation(req: http.ServerRequest, res: http.ServerRespons switch (operation) { case RequestType.GetDir: const filesInFolder = dir(reqPath, "", { recursive: true }); - send("success", res, filesInFolder.join(",")); + send(ResponseCode.Success, res, filesInFolder.join(",")); break; case RequestType.GetFile: fs.readFile(reqPath, (err, file) => { const contentType = contentTypeForExtension(path.extname(reqPath)); if (err) { - send("fail", res, err.message, contentType); + send(ResponseCode.NotFound, res, err.message, contentType); } else { - send("success", res, file, contentType); + send(ResponseCode.Success, res, file, contentType); } }); break; @@ -222,33 +224,33 @@ function handleRequestOperation(req: http.ServerRequest, res: http.ServerRespons processPost(req, res, (data) => { writeFile(reqPath, data, { recursive: true }); }); - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.WriteDir: fs.mkdirSync(reqPath); - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.DeleteFile: if (fs.existsSync(reqPath)) { fs.unlinkSync(reqPath); } - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.DeleteDir: if (fs.existsSync(reqPath)) { fs.rmdirSync(reqPath); } - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.AppendFile: processPost(req, res, (data) => { fs.appendFileSync(reqPath, data); }); - send("success", res, undefined); + send(ResponseCode.Success, res, undefined); break; case RequestType.Unknown: default: - send("unknown", res, undefined); + send(ResponseCode.BadRequest, res, undefined); break; } diff --git a/tslint.json b/tslint.json index a46e58f8c29..f058eab3dd6 100644 --- a/tslint.json +++ b/tslint.json @@ -32,7 +32,7 @@ "property-declaration": "nospace", "variable-declaration": "nospace" }], - "next-line": [true, + "next-line": [true, "check-catch", "check-else" ], @@ -44,7 +44,6 @@ "boolean-trivia": true, "type-operator-spacing": true, "prefer-const": true, - "no-in-operator": true, "no-increment-decrement": true, "object-literal-surrounding-space": true, "no-type-assertion-whitespace": true