diff --git a/Jakefile.js b/Jakefile.js index 3540655af4a..151f1ab5db4 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -5,6 +5,7 @@ var os = require("os"); var path = require("path"); var child_process = require("child_process"); var Linter = require("tslint"); +var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel; // Variables var compilerDirectory = "src/compiler/"; @@ -312,10 +313,7 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts } if (useDebugMode) { - options += " -sourcemap"; - if (!opts.noMapRoot) { - options += " -mapRoot file:///" + path.resolve(path.dirname(outFile)); - } + options += " --inlineSourceMap --inlineSources"; } else { options += " --newLine LF"; } @@ -485,7 +483,6 @@ var tscFile = path.join(builtLocalDirectory, compilerFilename); compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false); var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js"); -var servicesFileInBrowserTest = path.join(builtLocalDirectory, "typescriptServicesInBrowserTest.js"); var standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts"); var nodePackageFile = path.join(builtLocalDirectory, "typescript.js"); var nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts"); @@ -517,16 +514,6 @@ compileFile(servicesFile, servicesSources,[builtLocalDirectory, copyright].conca fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents); }); -compileFile(servicesFileInBrowserTest, servicesSources,[builtLocalDirectory, copyright].concat(servicesSources), - /*prefixes*/ [copyright], - /*useBuiltCompiler*/ true, - { noOutFile: false, generateDeclarations: true, preserveConstEnums: true, keepComments: true, noResolve: false, stripInternal: true, noMapRoot: true }, - /*callback*/ function () { - var content = fs.readFileSync(servicesFileInBrowserTest).toString(); - var i = content.lastIndexOf("\n"); - fs.writeFileSync(servicesFileInBrowserTest, content.substring(0, i) + "\r\n//# sourceURL=../built/local/typeScriptServices.js" + content.substring(i)); - }); - var serverFile = path.join(builtLocalDirectory, "tsserver.js"); compileFile(serverFile, serverSources,[builtLocalDirectory, copyright].concat(serverSources), /*prefixes*/ [copyright], /*useBuiltCompiler*/ true); @@ -688,7 +675,6 @@ function cleanTestDirs() { // used to pass data from jake command line directly to run.js function writeTestConfigFile(tests, light, taskConfigsFolder, workerCount) { var testConfigContents = JSON.stringify({ test: tests ? [tests] : undefined, light: light, workerCount: workerCount, taskConfigsFolder: taskConfigsFolder }); - console.log('Running tests with config: ' + testConfigContents); fs.writeFileSync('test.config', testConfigContents); } @@ -749,42 +735,16 @@ function runConsoleTests(defaultReporter, runInParallel) { } else { - // run task to load all tests and partition them between workers - var cmd = "mocha " + " -R min " + colors + run; - console.log(cmd); - exec(cmd, function() { - // read all configuration files and spawn a worker for every config - var configFiles = fs.readdirSync(taskConfigsFolder); - var counter = configFiles.length; - var firstErrorStatus; - // schedule work for chunks - configFiles.forEach(function (f) { - var configPath = path.join(taskConfigsFolder, f); - var workerCmd = "mocha" + " -t " + testTimeout + " -R " + reporter + " " + colors + " " + run + " --config='" + configPath + "'"; - console.log(workerCmd); - exec(workerCmd, finishWorker, finishWorker) - }); - - function finishWorker(e, errorStatus) { - counter--; - if (firstErrorStatus === undefined && errorStatus !== undefined) { - firstErrorStatus = errorStatus; - } - if (counter !== 0) { - complete(); - } - else { - // last worker clean everything and runs linter in case if there were no errors - deleteTemporaryProjectOutput(); - jake.rmRf(taskConfigsFolder); - if (firstErrorStatus === undefined) { - runLinter(); - complete(); - } - else { - failWithStatus(firstErrorStatus); - } - } + runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) { + // last worker clean everything and runs linter in case if there were no errors + deleteTemporaryProjectOutput(); + jake.rmRf(taskConfigsFolder); + if (err) { + fail(err); + } + else { + runLinter(); + complete(); } }); } @@ -839,12 +799,12 @@ compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile desc("Runs browserify on run.js to produce a file suitable for running tests in the browser"); task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function() { - var cmd = 'browserify built/local/run.js -o built/local/bundle.js'; + var cmd = 'browserify built/local/run.js -d -o built/local/bundle.js'; exec(cmd); }, {async: true}); desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], port=, browser=[chrome|IE]"); -task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function() { +task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFile], function() { cleanTestDirs(); host = "node"; port = process.env.port || process.env.p || '8888'; diff --git a/scripts/mocha-none-reporter.js b/scripts/mocha-none-reporter.js new file mode 100644 index 00000000000..5787b0c042e --- /dev/null +++ b/scripts/mocha-none-reporter.js @@ -0,0 +1,26 @@ +/** + * Module dependencies. + */ + +var Base = require('mocha').reporters.Base; + +/** + * Expose `None`. + */ + +exports = module.exports = None; + +/** + * Initialize a new `None` test reporter. + * + * @api public + * @param {Runner} runner + */ +function None(runner) { + Base.call(this); +} + +/** + * Inherit from `Base.prototype`. + */ +None.prototype.__proto__ = Base.prototype; diff --git a/scripts/mocha-parallel.js b/scripts/mocha-parallel.js new file mode 100644 index 00000000000..159dd630cc2 --- /dev/null +++ b/scripts/mocha-parallel.js @@ -0,0 +1,367 @@ +var tty = require("tty") + , readline = require("readline") + , fs = require("fs") + , path = require("path") + , child_process = require("child_process") + , os = require("os") + , mocha = require("mocha") + , Base = mocha.reporters.Base + , color = Base.color + , cursor = Base.cursor + , ms = require("mocha/lib/ms"); + +var isatty = tty.isatty(1) && tty.isatty(2); +var tapRangePattern = /^(\d+)\.\.(\d+)(?:$|\r\n?|\n)/; +var tapTestPattern = /^(not\sok|ok)\s+(\d+)\s+(?:-\s+)?(.*)$/; +var tapCommentPattern = /^#(?: (tests|pass|fail) (\d+)$)?/; + +exports.runTestsInParallel = runTestsInParallel; +exports.ProgressBars = ProgressBars; + +function runTestsInParallel(taskConfigsFolder, run, options, cb) { + if (options === undefined) options = { }; + + return discoverTests(run, options, function (error) { + if (error) { + return cb(error); + } + + return runTests(taskConfigsFolder, run, options, cb); + }); +} + +function discoverTests(run, options, cb) { + console.log("Discovering tests..."); + + var cmd = "mocha -R " + require.resolve("./mocha-none-reporter.js") + " " + run; + var p = child_process.spawn( + process.platform === "win32" ? "cmd" : "/bin/sh", + process.platform === "win32" ? ["/c", cmd] : ["-c", cmd], { + windowsVerbatimArguments: true, + env: { NODE_ENV: "development" } + }); + + p.on("exit", function (status) { + if (status) { + cb(new Error("Process exited with code " + status)); + } + else { + cb(); + } + }); +} + +function runTests(taskConfigsFolder, run, options, cb) { + var configFiles = fs.readdirSync(taskConfigsFolder); + var numPartitions = configFiles.length; + if (numPartitions <= 0) { + cb(); + return; + } + + console.log("Running tests on " + numPartitions + " threads..."); + + var partitions = Array(numPartitions); + var progressBars = new ProgressBars(); + progressBars.enable(); + + var counter = numPartitions; + configFiles.forEach(runTestsInPartition); + + function runTestsInPartition(file, index) { + var partition = { + file: path.join(taskConfigsFolder, file), + tests: 0, + passed: 0, + failed: 0, + completed: 0, + current: undefined, + start: undefined, + end: undefined, + failures: [] + }; + partitions[index] = partition; + + // Set up the progress bar. + updateProgress(0); + + // Start the background process. + var cmd = "mocha -t " + (options.testTimeout || 20000) + " -R tap --no-colors " + run + " --config='" + partition.file + "'"; + var p = child_process.spawn( + process.platform === "win32" ? "cmd" : "/bin/sh", + process.platform === "win32" ? ["/c", cmd] : ["-c", cmd], { + windowsVerbatimArguments: true, + env: { NODE_ENV: "development" } + }); + + var rl = readline.createInterface({ + input: p.stdout, + terminal: false + }); + + rl.on("line", onmessage); + p.on("exit", onexit) + + function onmessage(line) { + if (partition.start === undefined) { + partition.start = Date.now(); + } + + var rangeMatch = tapRangePattern.exec(line); + if (rangeMatch) { + partition.tests = parseInt(rangeMatch[2]); + return; + } + + var testMatch = tapTestPattern.exec(line); + if (testMatch) { + var test = { + result: testMatch[1], + id: parseInt(testMatch[2]), + name: testMatch[3], + output: [] + }; + + partition.current = test; + partition.completed++; + + if (test.result === "ok") { + partition.passed++; + } + else { + partition.failed++; + partition.failures.push(test); + } + + var progress = partition.completed / partition.tests; + if (progress < 1) { + updateProgress(progress); + } + + return; + } + + var commentMatch = tapCommentPattern.exec(line); + if (commentMatch) { + switch (commentMatch[1]) { + case "tests": + partition.current = undefined; + partition.tests = parseInt(commentMatch[2]); + break; + + case "pass": + partition.passed = parseInt(commentMatch[2]); + break; + + case "fail": + partition.failed = parseInt(commentMatch[2]); + break; + } + + return; + } + + if (partition.current) { + partition.current.output.push(line); + } + } + + function onexit() { + if (partition.end === undefined) { + partition.end = Date.now(); + } + + partition.duration = partition.end - partition.start; + var summaryColor = partition.failed ? "fail" : "green"; + var summarySymbol = partition.failed ? Base.symbols.err : Base.symbols.ok; + var summaryTests = (partition.passed === partition.tests ? partition.passed : partition.passed + "/" + partition.tests) + " passing"; + var summaryDuration = "(" + ms(partition.duration) + ")"; + var savedUseColors = Base.useColors; + Base.useColors = !options.noColors; + + var summary = color(summaryColor, summarySymbol + " " + summaryTests) + " " + color("light", summaryDuration); + Base.useColors = savedUseColors; + + updateProgress(1, summary); + + signal(); + } + + function updateProgress(percentComplete, title) { + var progressColor = "pending"; + if (partition.failed) { + progressColor = "fail"; + } + + progressBars.update( + index, + percentComplete, + progressColor, + title + ); + } + } + + function signal() { + counter--; + + if (counter <= 0) { + var failed = 0; + var reporter = new Base(), + stats = reporter.stats, + failures = reporter.failures; + + var duration = 0; + for (var i = 0; i < numPartitions; i++) { + var partition = partitions[i]; + stats.passes += partition.passed; + stats.failures += partition.failed; + stats.tests += partition.tests; + duration += partition.duration; + for (var j = 0; j < partition.failures.length; j++) { + var failure = partition.failures[j]; + failures.push(makeMochaTest(failure)); + } + } + + stats.duration = duration; + progressBars.disable(); + + if (options.noColors) { + var savedUseColors = Base.useColors; + Base.useColors = false; + reporter.epilogue(); + Base.useColors = savedUseColors; + } + else { + reporter.epilogue(); + } + + if (failed) { + return cb(new Error("Test failures reported: " + failed)); + } + else { + return cb(); + } + } + } + + function makeMochaTest(test) { + return { + fullTitle: function() { + return test.name; + }, + err: { + message: test.output[0], + stack: test.output.join(os.EOL) + } + }; + } +} + +function ProgressBars(options) { + if (!options) options = {}; + var open = options.open || '['; + var close = options.close || ']'; + var complete = options.complete || '▬'; + var incomplete = options.incomplete || Base.symbols.dot; + var maxWidth = Math.floor(Base.window.width * .30) - open.length - close.length - 2; + var width = minMax(options.width || maxWidth, 10, maxWidth); + this._options = { + open: open, + complete: complete, + incomplete: incomplete, + close: close, + width: width + }; + + this._progressBars = []; + this._lineCount = 0; + this._enabled = false; +} +ProgressBars.prototype = { + enable: function () { + if (!this._enabled) { + process.stdout.write(os.EOL); + this._enabled = true; + } + }, + disable: function () { + if (this._enabled) { + process.stdout.write(os.EOL); + this._enabled = false; + } + }, + update: function (index, percentComplete, color, title) { + percentComplete = minMax(percentComplete, 0, 1); + + var progressBar = this._progressBars[index] || (this._progressBars[index] = { }); + var width = this._options.width; + var n = Math.floor(width * percentComplete); + var i = width - n; + if (n === progressBar.lastN && title === progressBar.title && color === progressBar.progressColor) { + return; + } + + progressBar.lastN = n; + progressBar.title = title; + progressBar.progressColor = color; + + var progress = " "; + progress += this._color('progress', this._options.open); + progress += this._color(color, fill(this._options.complete, n)); + progress += this._color('progress', fill(this._options.incomplete, i)); + progress += this._color('progress', this._options.close); + + if (title) { + progress += this._color('progress', ' ' + title); + } + + if (progressBar.text !== progress) { + progressBar.text = progress; + this._render(index); + } + }, + _render: function (index) { + if (!this._enabled || !isatty) { + return; + } + + cursor.hide(); + readline.moveCursor(process.stdout, -process.stdout.columns, -this._lineCount); + var lineCount = 0; + var numProgressBars = this._progressBars.length; + for (var i = 0; i < numProgressBars; i++) { + if (i === index) { + readline.clearLine(process.stdout, 1); + process.stdout.write(this._progressBars[i].text + os.EOL); + } + else { + readline.moveCursor(process.stdout, -process.stdout.columns, +1); + } + + lineCount++; + } + + this._lineCount = lineCount; + cursor.show(); + }, + _color: function (type, text) { + return type && !this._options.noColors ? color(type, text) : text; + } +}; + +function fill(ch, size) { + var s = ""; + while (s.length < size) { + s += ch; + } + + return s.length > size ? s.substr(0, size) : s; +} + +function minMax(value, min, max) { + if (value < min) return min; + if (value > max) return max; + return value; +} \ No newline at end of file diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 96213801264..cd9246c2d21 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1198,9 +1198,9 @@ namespace ts { lastContainer = next; } - function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): void { + function declareSymbolAndAddToSymbolTable(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { // Just call this directly so that the return type of this function stays "void". - declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); + return declareSymbolAndAddToSymbolTableWorker(node, symbolFlags, symbolExcludes); } function declareSymbolAndAddToSymbolTableWorker(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags): Symbol { @@ -1304,7 +1304,22 @@ namespace ts { declareSymbolAndAddToSymbolTable(node, SymbolFlags.NamespaceModule, SymbolFlags.NamespaceModuleExcludes); } else { - declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + let pattern: Pattern | undefined; + if (node.name.kind === SyntaxKind.StringLiteral) { + const text = (node.name).text; + if (hasZeroOrOneAsteriskCharacter(text)) { + pattern = tryParsePattern(text); + } + else { + errorOnFirstToken(node.name, Diagnostics.Pattern_0_can_have_at_most_one_Asterisk_character, text); + } + } + + const symbol = declareSymbolAndAddToSymbolTable(node, SymbolFlags.ValueModule, SymbolFlags.ValueModuleExcludes); + + if (pattern) { + (file.patternAmbientModules || (file.patternAmbientModules = [])).push({ pattern, symbol }); + } } } else { @@ -2082,10 +2097,10 @@ namespace ts { checkStrictModeFunctionName(node); if (inStrictMode) { checkStrictModeFunctionDeclaration(node); - return bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + bindBlockScopedDeclaration(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); } else { - return declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); + declareSymbolAndAddToSymbolTable(node, SymbolFlags.Function, SymbolFlags.FunctionExcludes); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 281bac29914..c4a0a85175e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -140,6 +140,12 @@ namespace ts { const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); const globals: SymbolTable = {}; + /** + * 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. + * This is only used if there is no exact match. + */ + let patternAmbientModules: PatternAmbientModule[]; let getGlobalESSymbolConstructorSymbol: () => Symbol; @@ -1285,6 +1291,14 @@ namespace ts { } return undefined; } + + if (patternAmbientModules) { + const pattern = findBestPatternMatch(patternAmbientModules, _ => _.pattern, moduleName); + if (pattern) { + return getMergedSymbol(pattern.symbol); + } + } + if (moduleNotFoundError) { // report errors only if it was requested error(moduleReferenceLiteral, moduleNotFoundError, moduleName); @@ -2844,7 +2858,7 @@ namespace ts { } // In strict null checking mode, if a default value of a non-undefined type is specified, remove // undefined from the final type. - if (strictNullChecks && declaration.initializer && !(getNullableKind(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { + if (strictNullChecks && declaration.initializer && !(getCombinedTypeFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) { type = getTypeWithFacts(type, TypeFacts.NEUndefined); } return type; @@ -2887,7 +2901,7 @@ namespace ts { } function addOptionality(type: Type, optional: boolean): Type { - return strictNullChecks && optional ? addNullableKind(type, TypeFlags.Undefined) : type; + return strictNullChecks && optional ? addTypeKind(type, TypeFlags.Undefined) : type; } // Return the inferred type for a variable, parameter, or property declaration @@ -3222,7 +3236,7 @@ namespace ts { if (!links.type) { const type = createObjectType(TypeFlags.Anonymous, symbol); links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ? - addNullableKind(type, TypeFlags.Undefined) : type; + addTypeKind(type, TypeFlags.Undefined) : type; } return links.type; } @@ -6746,7 +6760,7 @@ namespace ts { return getUnionType(types); } const supertype = forEach(primaryTypes, t => isSupertypeOfEach(t, primaryTypes) ? t : undefined); - return supertype && addNullableKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable); + return supertype && addTypeKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable); } function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void { @@ -6817,28 +6831,22 @@ namespace ts { return !!(type.flags & TypeFlags.Tuple); } - function getNullableKind(type: Type): TypeFlags { - let flags = type.flags; - if (flags & TypeFlags.Union) { - for (const t of (type as UnionType).types) { - flags |= t.flags; - } - } - return flags & TypeFlags.Nullable; + function getCombinedTypeFlags(type: Type): TypeFlags { + return type.flags & TypeFlags.Union ? getCombinedFlagsOfTypes((type).types) : type.flags; } - function addNullableKind(type: Type, kind: TypeFlags): Type { - if ((getNullableKind(type) & kind) !== kind) { - const types = [type]; - if (kind & TypeFlags.Undefined) { - types.push(undefinedType); - } - if (kind & TypeFlags.Null) { - types.push(nullType); - } - type = getUnionType(types); + function addTypeKind(type: Type, kind: TypeFlags) { + if ((getCombinedTypeFlags(type) & kind) === kind) { + return type; } - return type; + const types = [type]; + if (kind & TypeFlags.String) types.push(stringType); + if (kind & TypeFlags.Number) types.push(numberType); + if (kind & TypeFlags.Boolean) types.push(booleanType); + if (kind & TypeFlags.Void) types.push(voidType); + if (kind & TypeFlags.Undefined) types.push(undefinedType); + if (kind & TypeFlags.Null) types.push(nullType); + return getUnionType(types); } function getNonNullableType(type: Type): Type { @@ -7662,12 +7670,27 @@ namespace ts { getInitialTypeOfBindingElement(node); } + function getReferenceFromExpression(node: Expression): Expression { + switch (node.kind) { + case SyntaxKind.ParenthesizedExpression: + return getReferenceFromExpression((node).expression); + case SyntaxKind.BinaryExpression: + switch ((node).operatorToken.kind) { + case SyntaxKind.EqualsToken: + return getReferenceFromExpression((node).left); + case SyntaxKind.CommaToken: + return getReferenceFromExpression((node).right); + } + } + return node; + } + function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) { let key: string; if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) { return declaredType; } - const initialType = assumeInitialized ? declaredType : addNullableKind(declaredType, TypeFlags.Undefined); + const initialType = assumeInitialized ? declaredType : addTypeKind(declaredType, TypeFlags.Undefined); const visitedFlowStart = visitedFlowCount; const result = getTypeAtFlowNode(reference.flowNode); visitedFlowCount = visitedFlowStart; @@ -7885,7 +7908,7 @@ namespace ts { if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) { assumeTrue = !assumeTrue; } - if (!strictNullChecks || !isMatchingReference(reference, narrowed)) { + if (!strictNullChecks || !isMatchingReference(reference, getReferenceFromExpression(narrowed))) { return type; } const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken; @@ -7900,12 +7923,12 @@ namespace ts { function narrowTypeByTypeof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { // We have '==', '!=', '====', or !==' operator with 'typeof xxx' on the left // and string literal on the right - const typeOf = (expr.left.kind === SyntaxKind.TypeOfExpression ? expr.left : expr.right); + const narrowed = getReferenceFromExpression(((expr.left.kind === SyntaxKind.TypeOfExpression ? expr.left : expr.right)).expression); const literal = (expr.right.kind === SyntaxKind.StringLiteral ? expr.right : expr.left); - if (!isMatchingReference(reference, typeOf.expression)) { + if (!isMatchingReference(reference, narrowed)) { // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, typeOf.expression)) { + if (containsMatchingReference(reference, narrowed)) { return declaredType; } return type; @@ -7930,10 +7953,11 @@ namespace ts { } function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { - if (!isMatchingReference(reference, expr.left)) { + const left = getReferenceFromExpression(expr.left); + if (!isMatchingReference(reference, left)) { // For a reference of the form 'x.y', an 'x instanceof T' type guard resets the // narrowed type of 'y' to its declared type. - if (containsMatchingReference(reference, expr.left)) { + if (containsMatchingReference(reference, left)) { return declaredType; } return type; @@ -8000,7 +8024,7 @@ namespace ts { const targetType = type.flags & TypeFlags.TypeParameter ? getApparentType(type) : type; return isTypeAssignableTo(candidate, targetType) ? candidate : isTypeAssignableTo(type, candidate) ? type : - neverType; + getIntersectionType([type, candidate]); } function narrowTypeByTypePredicate(type: Type, callExpression: CallExpression, assumeTrue: boolean): Type { @@ -8175,7 +8199,7 @@ namespace ts { getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) || !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions); const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions); - if (!assumeInitialized && !(getNullableKind(type) & TypeFlags.Undefined) && getNullableKind(flowType) & TypeFlags.Undefined) { + if (!assumeInitialized && !(getCombinedTypeFlags(type) & TypeFlags.Undefined) && getCombinedTypeFlags(flowType) & TypeFlags.Undefined) { error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol)); // Return the declared type to reduce follow-on errors return type; @@ -8749,6 +8773,16 @@ namespace ts { function getContextualTypeForReturnExpression(node: Expression): Type { const func = getContainingFunction(node); + + if (isAsyncFunctionLike(func)) { + const contextualReturnType = getContextualReturnType(func); + if (contextualReturnType) { + return getPromisedType(contextualReturnType); + } + + return undefined; + } + if (func && !func.asteriskToken) { return getContextualReturnType(func); } @@ -9957,7 +9991,7 @@ namespace ts { function checkNonNullExpression(node: Expression | QualifiedName) { const type = checkExpression(node); if (strictNullChecks) { - const kind = getNullableKind(type); + const kind = getCombinedTypeFlags(type) & TypeFlags.Nullable; if (kind) { error(node, kind & TypeFlags.Undefined ? kind & TypeFlags.Null ? Diagnostics.Object_is_possibly_null_or_undefined : @@ -11491,7 +11525,7 @@ namespace ts { if (strictNullChecks) { const declaration = symbol.valueDeclaration; if (declaration && (declaration).initializer) { - return addNullableKind(type, TypeFlags.Undefined); + return addTypeKind(type, TypeFlags.Undefined); } } return type; @@ -12417,7 +12451,7 @@ namespace ts { case SyntaxKind.InKeyword: return checkInExpression(left, right, leftType, rightType); case SyntaxKind.AmpersandAmpersandToken: - return strictNullChecks ? addNullableKind(rightType, getNullableKind(leftType)) : rightType; + return strictNullChecks ? addTypeKind(rightType, getCombinedTypeFlags(leftType) & TypeFlags.Falsy) : rightType; case SyntaxKind.BarBarToken: return getUnionType([getNonNullableType(leftType), rightType]); case SyntaxKind.EqualsToken: @@ -16866,10 +16900,7 @@ namespace ts { return getIntrinsicTagSymbol(entityName.parent); } - // Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead - // return the alias symbol. - const meaning: SymbolFlags = SymbolFlags.Value | SymbolFlags.Alias; - return resolveEntityName(entityName, meaning); + return resolveEntityName(entityName, SymbolFlags.Value, /*ignoreErrors*/ false, /*dontResolveAlias*/ true); } else if (entityName.kind === SyntaxKind.PropertyAccessExpression) { const symbol = getNodeLinks(entityName).resolvedSymbol; @@ -16887,11 +16918,8 @@ namespace ts { } } else if (isTypeReferenceIdentifier(entityName)) { - let meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace; - // Include aliases in the meaning, this ensures that we do not follow aliases to where they point and instead - // return the alias symbol. - meaning |= SymbolFlags.Alias; - return resolveEntityName(entityName, meaning); + const meaning = (entityName.parent.kind === SyntaxKind.TypeReference || entityName.parent.kind === SyntaxKind.JSDocTypeReference) ? SymbolFlags.Type : SymbolFlags.Namespace; + return resolveEntityName(entityName, meaning, /*ignoreErrors*/ false, /*dontResolveAlias*/true); } else if (entityName.parent.kind === SyntaxKind.JsxAttribute) { return getJsxAttributePropertySymbol(entityName.parent); @@ -17635,6 +17663,10 @@ namespace ts { if (!isExternalOrCommonJsModule(file)) { mergeSymbolTable(globals, file.locals); } + if (file.patternAmbientModules && file.patternAmbientModules.length) { + patternAmbientModules = concatenate(patternAmbientModules, file.patternAmbientModules); + } + if (file.moduleAugmentations.length) { (augmentations || (augmentations = [])).push(file.moduleAugmentations); } @@ -17765,6 +17797,8 @@ namespace ts { case SyntaxKind.ImportEqualsDeclaration: case SyntaxKind.ExportDeclaration: case SyntaxKind.ExportAssignment: + case SyntaxKind.FunctionExpression: + case SyntaxKind.ArrowFunction: case SyntaxKind.Parameter: break; case SyntaxKind.FunctionDeclaration: diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 7be050ccef5..361b38d402b 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -6,7 +6,7 @@ namespace ts { /* @internal */ - export let optionDeclarations: CommandLineOption[] = [ + export const optionDeclarations: CommandLineOption[] = [ { name: "charset", type: "string", diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 79b6249539d..8a2040a83df 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -849,13 +849,22 @@ namespace ts { const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"]; export function removeFileExtension(path: string): string { for (const ext of extensionsToRemove) { - if (fileExtensionIs(path, ext)) { - return path.substr(0, path.length - ext.length); + const extensionless = tryRemoveExtension(path, ext); + if (extensionless !== undefined) { + return extensionless; } } return path; } + export function tryRemoveExtension(path: string, extension: string): string { + return fileExtensionIs(path, extension) ? path.substring(0, path.length - extension.length) : undefined; + } + + export function isJsxOrTsxExtension(ext: string): boolean { + return ext === ".jsx" || ext === ".tsx"; + } + export interface ObjectAllocator { getNodeConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => Node; getSourceFileConstructor(): new (kind: SyntaxKind, pos?: number, end?: number) => SourceFile; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 034bd27b147..c27612764d1 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2260,7 +2260,7 @@ "category": "Error", "code": 5047 }, - "Option 'inlineSources' can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": { + "Option '{0} can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided.": { "category": "Error", "code": 5051 }, @@ -2772,6 +2772,10 @@ "category": "Error", "code": 6131 }, + "File name '{0}' has a '{1}' extension - stripping it": { + "category": "Message", + "code": 6132 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 5b64d066da1..2535afb93a6 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -95,7 +95,8 @@ namespace ts { return compilerOptions.traceResolution && host.trace !== undefined; } - function hasZeroOrOneAsteriskCharacter(str: string): boolean { + /* @internal */ + export function hasZeroOrOneAsteriskCharacter(str: string): boolean { let seenAsterisk = false; for (let i = 0; i < str.length; i++) { if (str.charCodeAt(i) === CharacterCodes.asterisk) { @@ -496,48 +497,23 @@ namespace ts { trace(state.host, Diagnostics.baseUrl_option_is_set_to_0_using_this_value_to_resolve_non_relative_module_name_1, state.compilerOptions.baseUrl, moduleName); } - let longestMatchPrefixLength = -1; - let matchedPattern: string; - let matchedStar: string; - + // string is for exact match + let matchedPattern: Pattern | string | undefined = undefined; if (state.compilerOptions.paths) { if (state.traceEnabled) { trace(state.host, Diagnostics.paths_option_is_specified_looking_for_a_pattern_to_match_module_name_0, moduleName); } - - for (const key in state.compilerOptions.paths) { - const pattern: string = key; - const indexOfStar = pattern.indexOf("*"); - if (indexOfStar !== -1) { - const prefix = pattern.substr(0, indexOfStar); - const suffix = pattern.substr(indexOfStar + 1); - if (moduleName.length >= prefix.length + suffix.length && - startsWith(moduleName, prefix) && - endsWith(moduleName, suffix)) { - - // use length of prefix as betterness criteria - if (prefix.length > longestMatchPrefixLength) { - longestMatchPrefixLength = prefix.length; - matchedPattern = pattern; - matchedStar = moduleName.substr(prefix.length, moduleName.length - suffix.length); - } - } - } - else if (pattern === moduleName) { - // pattern was matched as is - no need to search further - matchedPattern = pattern; - matchedStar = undefined; - break; - } - } + matchedPattern = matchPatternOrExact(getKeys(state.compilerOptions.paths), moduleName); } if (matchedPattern) { + const matchedStar = typeof matchedPattern === "string" ? undefined : matchedText(matchedPattern, moduleName); + const matchedPatternText = typeof matchedPattern === "string" ? matchedPattern : patternText(matchedPattern); if (state.traceEnabled) { - trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPattern); + trace(state.host, Diagnostics.Module_name_0_matched_pattern_1, moduleName, matchedPatternText); } - for (const subst of state.compilerOptions.paths[matchedPattern]) { - const path = matchedStar ? subst.replace("\*", matchedStar) : subst; + for (const subst of state.compilerOptions.paths[matchedPatternText]) { + const path = matchedStar ? subst.replace("*", matchedStar) : subst; const candidate = normalizePath(combinePaths(state.compilerOptions.baseUrl, path)); if (state.traceEnabled) { trace(state.host, Diagnostics.Trying_substitution_0_candidate_module_location_Colon_1, subst, path); @@ -560,6 +536,75 @@ namespace ts { } } + /** + * patternStrings contains both pattern strings (containing "*") and regular strings. + * Return an exact match if possible, or a pattern match, or undefined. + * (These are verified by verifyCompilerOptions to have 0 or 1 "*" characters.) + */ + function matchPatternOrExact(patternStrings: string[], candidate: string): string | Pattern | undefined { + const patterns: Pattern[] = []; + for (const patternString of patternStrings) { + const pattern = tryParsePattern(patternString); + if (pattern) { + patterns.push(pattern); + } + else if (patternString === candidate) { + // pattern was matched as is - no need to search further + return patternString; + } + } + + return findBestPatternMatch(patterns, _ => _, candidate); + } + + function patternText({prefix, suffix}: Pattern): string { + return `${prefix}*${suffix}`; + } + + /** + * Given that candidate matches pattern, returns the text matching the '*'. + * E.g.: matchedText(tryParsePattern("foo*baz"), "foobarbaz") === "bar" + */ + function matchedText(pattern: Pattern, candidate: string): string { + Debug.assert(isPatternMatch(pattern, candidate)); + return candidate.substr(pattern.prefix.length, candidate.length - pattern.suffix.length); + } + + /** Return the object corresponding to the best pattern to match `candidate`. */ + /* @internal */ + export function findBestPatternMatch(values: T[], getPattern: (value: T) => Pattern, candidate: string): T | undefined { + let matchedValue: T | undefined = undefined; + // use length of prefix as betterness criteria + let longestMatchPrefixLength = -1; + + for (const v of values) { + const pattern = getPattern(v); + if (isPatternMatch(pattern, candidate) && pattern.prefix.length > longestMatchPrefixLength) { + longestMatchPrefixLength = pattern.prefix.length; + matchedValue = v; + } + } + + return matchedValue; + } + + function isPatternMatch({prefix, suffix}: Pattern, candidate: string) { + return candidate.length >= prefix.length + suffix.length && + startsWith(candidate, prefix) && + endsWith(candidate, suffix); + } + + /* @internal */ + export function tryParsePattern(pattern: string): Pattern | undefined { + // This should be verified outside of here and a proper error thrown. + Debug.assert(hasZeroOrOneAsteriskCharacter(pattern)); + const indexOfStar = pattern.indexOf("*"); + return indexOfStar === -1 ? undefined : { + prefix: pattern.substr(0, indexOfStar), + suffix: pattern.substr(indexOfStar + 1) + }; + } + export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations { const containingDirectory = getDirectoryPath(containingFile); const supportedExtensions = getSupportedExtensions(compilerOptions); @@ -619,8 +664,25 @@ namespace ts { * in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations. */ function loadModuleFromFile(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { + // First try to keep/add an extension: importing "./foo.ts" can be matched by a file "./foo.ts", and "./foo" by "./foo.d.ts" + const resolvedByAddingOrKeepingExtension = loadModuleFromFileWorker(candidate, extensions, failedLookupLocation, onlyRecordFailures, state); + if (resolvedByAddingOrKeepingExtension) { + return resolvedByAddingOrKeepingExtension; + } + // Then try stripping a ".js" or ".jsx" extension and replacing it with a TypeScript one, e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts" + if (hasJavaScriptFileExtension(candidate)) { + const extensionless = removeFileExtension(candidate); + if (state.traceEnabled) { + const extension = candidate.substring(extensionless.length); + trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension); + } + return loadModuleFromFileWorker(extensionless, extensions, failedLookupLocation, onlyRecordFailures, state); + } + } + + function loadModuleFromFileWorker(candidate: string, extensions: string[], failedLookupLocation: string[], onlyRecordFailures: boolean, state: ModuleResolutionState): string { if (!onlyRecordFailures) { - // check if containig folder exists - if it doesn't then just record failures for all supported extensions without disk probing + // check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing const directory = getDirectoryPath(candidate); if (directory) { onlyRecordFailures = !directoryProbablyExists(directory, state.host); @@ -629,7 +691,7 @@ namespace ts { return forEach(extensions, tryLoad); function tryLoad(ext: string): string { - if (ext === ".tsx" && state.skipTsx) { + if (state.skipTsx && isJsxOrTsxExtension(ext)) { return undefined; } const fileName = fileExtensionIs(candidate, ext) ? candidate : candidate + ext; @@ -2043,12 +2105,12 @@ namespace ts { } } - if (options.inlineSources) { - if (!options.sourceMap && !options.inlineSourceMap) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_inlineSources_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided)); + if (!options.sourceMap && !options.inlineSourceMap) { + if (options.inlineSources) { + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "inlineSources")); } if (options.sourceRoot) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "sourceRoot", "inlineSources")); + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_can_only_be_used_when_either_option_inlineSourceMap_or_option_sourceMap_is_provided, "sourceRoot")); } } @@ -2056,14 +2118,9 @@ namespace ts { programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_with_option_1, "out", "outFile")); } - if (!options.sourceMap && (options.mapRoot || options.sourceRoot)) { - // Error to specify --mapRoot or --sourceRoot without mapSourceFiles - if (options.mapRoot) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap")); - } - if (options.sourceRoot && !options.inlineSourceMap) { - programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "sourceRoot", "sourceMap")); - } + if (options.mapRoot && !options.sourceMap) { + // Error to specify --mapRoot without --sourcemap + programDiagnostics.add(createCompilerDiagnostic(Diagnostics.Option_0_cannot_be_specified_without_specifying_option_1, "mapRoot", "sourceMap")); } if (options.declarationDir) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index b5d8ee6db16..b6b4f532da5 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1658,6 +1658,7 @@ namespace ts { /* @internal */ resolvedTypeReferenceDirectiveNames: Map; /* @internal */ imports: LiteralExpression[]; /* @internal */ moduleAugmentations: LiteralExpression[]; + /* @internal */ patternAmbientModules?: PatternAmbientModule[]; } export interface ScriptReferenceHost { @@ -2135,6 +2136,20 @@ namespace ts { [index: string]: Symbol; } + /** Represents a "prefix*suffix" pattern. */ + /* @internal */ + export interface Pattern { + prefix: string; + suffix: string; + } + + /** Used to track a `declare module "foo*"`-like declaration. */ + /* @internal */ + export interface PatternAmbientModule { + pattern: Pattern; + symbol: Symbol; + } + /* @internal */ export const enum NodeCheckFlags { TypeChecked = 0x00000001, // Node has been type checked @@ -2209,6 +2224,7 @@ namespace ts { /* @internal */ Nullable = Undefined | Null, + Falsy = String | Number | Boolean | Void | Undefined | Null, /* @internal */ Intrinsic = Any | String | Number | Boolean | ESSymbol | Void | Undefined | Null | Never, /* @internal */ diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 5fe5b84ac91..b17abc613a3 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -1692,6 +1692,3 @@ namespace Harness { if (Error) (Error).stackTraceLimit = 1; } - -// TODO: not sure why Utils.evalFile isn't working with this, eventually will concat it like old compiler instead of eval -eval(Harness.tcServicesFile); diff --git a/src/server/session.ts b/src/server/session.ts index 7f3ba105999..0ec3d87e58c 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -1207,7 +1207,11 @@ namespace ts.server { // Handle cancellation exceptions } this.logError(err, message); - this.output(undefined, request ? request.command : CommandNames.Unknown, request ? request.seq : 0, "Error processing request. " + err.message); + this.output( + undefined, + request ? request.command : CommandNames.Unknown, + request ? request.seq : 0, + "Error processing request. " + (err).message + "\n" + (err).stack); } } } diff --git a/src/services/breakpoints.ts b/src/services/breakpoints.ts index fea4ae6bffa..e5a41991255 100644 --- a/src/services/breakpoints.ts +++ b/src/services/breakpoints.ts @@ -722,5 +722,5 @@ namespace ts.BreakpointResolver { return spanInNode(node.parent); } } - } + } } diff --git a/src/services/services.ts b/src/services/services.ts index d657f1d5209..cae2af356a0 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1064,10 +1064,10 @@ namespace ts { getCancellationToken?(): HostCancellationToken; getCurrentDirectory(): string; getDefaultLibFileName(options: CompilerOptions): string; - log? (s: string): void; - trace? (s: string): void; - error? (s: string): void; - useCaseSensitiveFileNames? (): boolean; + log?(s: string): void; + trace?(s: string): void; + error?(s: string): void; + useCaseSensitiveFileNames?(): boolean; /* * LS host can optionally implement this method if it wants to be completely in charge of module name resolution. @@ -1939,6 +1939,40 @@ namespace ts { sourceMapText?: string; } + + + let commandLineOptions_stringToEnum: CommandLineOptionOfCustomType[]; + + /** JS users may pass in string values for enum compiler options (such as ModuleKind), so convert. */ + function fixupCompilerOptions(options: CompilerOptions, diagnostics: Diagnostic[]): CompilerOptions { + // Lazily create this value to fix module loading errors. + commandLineOptions_stringToEnum = commandLineOptions_stringToEnum || filter(optionDeclarations, o => + typeof o.type === "object" && !forEachValue(> o.type, v => typeof v !== "number")); + + options = clone(options); + + for (const opt of commandLineOptions_stringToEnum) { + if (!hasProperty(options, opt.name)) { + continue; + } + + const value = options[opt.name]; + // Value should be a key of opt.type + if (typeof value === "string") { + // If value is not a string, this will fail + options[opt.name] = parseCustomTypeOption(opt, value, diagnostics); + } + else { + if (!forEachValue(opt.type, v => v === value)) { + // Supplied value isn't a valid enum value. + diagnostics.push(createCompilerDiagnosticForInvalidCustomType(opt)); + } + } + } + + return options; + } + /* * This function will compile source text from 'input' argument using specified compiler options. * If not options are provided - it will use a set of default compiler options. @@ -1949,7 +1983,9 @@ namespace ts { * - noResolve = true */ export function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput { - const options = transpileOptions.compilerOptions ? clone(transpileOptions.compilerOptions) : getDefaultCompilerOptions(); + const diagnostics: Diagnostic[] = []; + + const options: CompilerOptions = transpileOptions.compilerOptions ? fixupCompilerOptions(transpileOptions.compilerOptions, diagnostics) : getDefaultCompilerOptions(); options.isolatedModules = true; @@ -2007,9 +2043,7 @@ namespace ts { const program = createProgram([inputFileName], options, compilerHost); - let diagnostics: Diagnostic[]; if (transpileOptions.reportDiagnostics) { - diagnostics = []; addRange(/*to*/ diagnostics, /*from*/ program.getSyntacticDiagnostics(sourceFile)); addRange(/*to*/ diagnostics, /*from*/ program.getOptionsDiagnostics()); } @@ -2499,7 +2533,7 @@ namespace ts { } // should be start of dependency list - if (token !== SyntaxKind.OpenBracketToken) { + if (token !== SyntaxKind.OpenBracketToken) { return true; } @@ -4032,10 +4066,15 @@ namespace ts { } } - function getCompletionsAtPosition(fileName: string, position: number): CompletionInfo { synchronizeHostData(); + const sourceFile = getValidSourceFile(fileName); + + if (isInString(sourceFile, position)) { + return getStringLiteralCompletionEntries(sourceFile, position); + } + const completionData = getCompletionData(fileName, position); if (!completionData) { return undefined; @@ -4048,12 +4087,10 @@ namespace ts { return { isMemberCompletion: false, isNewIdentifierLocation: false, entries: getAllJsDocCompletionEntries() }; } - const sourceFile = getValidSourceFile(fileName); - const entries: CompletionEntry[] = []; if (isSourceFileJavaScript(sourceFile)) { - const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries); + const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ false); addRange(entries, getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames)); } else { @@ -4077,7 +4114,7 @@ namespace ts { } } - getCompletionEntriesFromSymbols(symbols, entries); + getCompletionEntriesFromSymbols(symbols, entries, location, /*performCharacterChecks*/ true); } // Add keywords if this is not a member completion list @@ -4127,11 +4164,11 @@ namespace ts { })); } - function createCompletionEntry(symbol: Symbol, location: Node): CompletionEntry { + function createCompletionEntry(symbol: Symbol, location: Node, performCharacterChecks: boolean): CompletionEntry { // Try to get a valid display name for this symbol, if we could not find one, then ignore it. // We would like to only show things that can be added after a dot, so for instance numeric properties can // not be accessed with a dot (a.1 <- invalid) - const displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, /*performCharacterChecks*/ true, location); + const displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, performCharacterChecks, location); if (!displayName) { return undefined; } @@ -4152,12 +4189,12 @@ namespace ts { }; } - function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[]): Map { + function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[], location: Node, performCharacterChecks: boolean): Map { const start = new Date().getTime(); const uniqueNames: Map = {}; if (symbols) { for (const symbol of symbols) { - const entry = createCompletionEntry(symbol, location); + const entry = createCompletionEntry(symbol, location, performCharacterChecks); if (entry) { const id = escapeIdentifier(entry.name); if (!lookUp(uniqueNames, id)) { @@ -4171,6 +4208,93 @@ namespace ts { log("getCompletionsAtPosition: getCompletionEntriesFromSymbols: " + (new Date().getTime() - start)); return uniqueNames; } + + function getStringLiteralCompletionEntries(sourceFile: SourceFile, position: number) { + const node = findPrecedingToken(position, sourceFile); + if (!node || node.kind !== SyntaxKind.StringLiteral) { + return undefined; + } + + const argumentInfo = SignatureHelp.getContainingArgumentInfo(node, position, sourceFile); + if (argumentInfo) { + // Get string literal completions from specialized signatures of the target + return getStringLiteralCompletionEntriesFromCallExpression(argumentInfo); + } + else if (isElementAccessExpression(node.parent) && node.parent.argumentExpression === node) { + // Get all names of properties on the expression + return getStringLiteralCompletionEntriesFromElementAccess(node.parent); + } + else { + // Otherwise, get the completions from the contextual type if one exists + return getStringLiteralCompletionEntriesFromContextualType(node); + } + } + + function getStringLiteralCompletionEntriesFromCallExpression(argumentInfo: SignatureHelp.ArgumentListInfo) { + const typeChecker = program.getTypeChecker(); + const candidates: Signature[] = []; + const entries: CompletionEntry[] = []; + + typeChecker.getResolvedSignature(argumentInfo.invocation, candidates); + + for (const candidate of candidates) { + if (candidate.parameters.length > argumentInfo.argumentIndex) { + const parameter = candidate.parameters[argumentInfo.argumentIndex]; + addStringLiteralCompletionsFromType(typeChecker.getTypeAtLocation(parameter.valueDeclaration), entries); + } + } + + if (entries.length) { + return { isMemberCompletion: false, isNewIdentifierLocation: true, entries }; + } + + return undefined; + } + + function getStringLiteralCompletionEntriesFromElementAccess(node: ElementAccessExpression) { + const typeChecker = program.getTypeChecker(); + const type = typeChecker.getTypeAtLocation(node.expression); + const entries: CompletionEntry[] = []; + if (type) { + getCompletionEntriesFromSymbols(type.getApparentProperties(), entries, node, /*performCharacterChecks*/false); + if (entries.length) { + return { isMemberCompletion: true, isNewIdentifierLocation: true, entries }; + } + } + return undefined; + } + + function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral) { + const typeChecker = program.getTypeChecker(); + const type = typeChecker.getContextualType(node); + if (type) { + const entries: CompletionEntry[] = []; + addStringLiteralCompletionsFromType(type, entries); + if (entries.length) { + return { isMemberCompletion: false, isNewIdentifierLocation: false, entries }; + } + } + return undefined; + } + + function addStringLiteralCompletionsFromType(type: Type, result: CompletionEntry[]): void { + if (!type) { + return; + } + if (type.flags & TypeFlags.Union) { + forEach((type).types, t => addStringLiteralCompletionsFromType(t, result)); + } + else { + if (type.flags & TypeFlags.StringLiteral) { + result.push({ + name: (type).text, + kindModifiers: ScriptElementKindModifier.none, + kind: ScriptElementKind.variableElement, + sortText: "0" + }); + } + } + } } function getCompletionEntryDetails(fileName: string, position: number, entryName: string): CompletionEntryDetails { @@ -4335,7 +4459,7 @@ namespace ts { // try get the call/construct signature from the type if it matches let callExpression: CallExpression; if (location.kind === SyntaxKind.CallExpression || location.kind === SyntaxKind.NewExpression) { - callExpression = location; + callExpression = location; } else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { callExpression = location.parent; diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 27077406489..b5df2b5af20 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -162,15 +162,16 @@ namespace ts.SignatureHelp { // // Did not find matching token // return null; // } + const emptyArray: any[] = []; - const enum ArgumentListKind { + export const enum ArgumentListKind { TypeArguments, CallArguments, TaggedTemplateArguments } - interface ArgumentListInfo { + export interface ArgumentListInfo { kind: ArgumentListKind; invocation: CallLikeExpression; argumentsSpan: TextSpan; @@ -188,7 +189,8 @@ namespace ts.SignatureHelp { return undefined; } - const argumentInfo = getContainingArgumentInfo(startingToken); + const argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile); + cancellationToken.throwIfCancellationRequested(); // Semantic filtering of signature help @@ -205,431 +207,432 @@ namespace ts.SignatureHelp { // We didn't have any sig help items produced by the TS compiler. If this is a JS // file, then see if we can figure out anything better. if (isSourceFileJavaScript(sourceFile)) { - return createJavaScriptSignatureHelpItems(argumentInfo); + return createJavaScriptSignatureHelpItems(argumentInfo, program); } return undefined; } - return createSignatureHelpItems(candidates, resolvedSignature, argumentInfo); + return createSignatureHelpItems(candidates, resolvedSignature, argumentInfo, typeChecker); + } - function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo): SignatureHelpItems { - if (argumentInfo.invocation.kind !== SyntaxKind.CallExpression) { - return undefined; - } + function createJavaScriptSignatureHelpItems(argumentInfo: ArgumentListInfo, program: Program): SignatureHelpItems { + if (argumentInfo.invocation.kind !== SyntaxKind.CallExpression) { + return undefined; + } - // See if we can find some symbol with the call expression name that has call signatures. - const callExpression = argumentInfo.invocation; - const expression = callExpression.expression; - const name = expression.kind === SyntaxKind.Identifier - ? expression - : expression.kind === SyntaxKind.PropertyAccessExpression - ? (expression).name - : undefined; + // See if we can find some symbol with the call expression name that has call signatures. + const callExpression = argumentInfo.invocation; + const expression = callExpression.expression; + const name = expression.kind === SyntaxKind.Identifier + ? expression + : expression.kind === SyntaxKind.PropertyAccessExpression + ? (expression).name + : undefined; - if (!name || !name.text) { - return undefined; - } + if (!name || !name.text) { + return undefined; + } - const typeChecker = program.getTypeChecker(); - for (const sourceFile of program.getSourceFiles()) { - const nameToDeclarations = sourceFile.getNamedDeclarations(); - const declarations = getProperty(nameToDeclarations, name.text); + const typeChecker = program.getTypeChecker(); + for (const sourceFile of program.getSourceFiles()) { + const nameToDeclarations = sourceFile.getNamedDeclarations(); + const declarations = getProperty(nameToDeclarations, name.text); - if (declarations) { - for (const declaration of declarations) { - const symbol = declaration.symbol; - if (symbol) { - const type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration); - if (type) { - const callSignatures = type.getCallSignatures(); - if (callSignatures && callSignatures.length) { - return createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo); - } + if (declarations) { + for (const declaration of declarations) { + const symbol = declaration.symbol; + if (symbol) { + const type = typeChecker.getTypeOfSymbolAtLocation(symbol, declaration); + if (type) { + const callSignatures = type.getCallSignatures(); + if (callSignatures && callSignatures.length) { + return createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, typeChecker); } } } } } } + } - /** - * Returns relevant information for the argument list and the current argument if we are - * in the argument of an invocation; returns undefined otherwise. - */ - function getImmediatelyContainingArgumentInfo(node: Node): ArgumentListInfo { - if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) { - const callExpression = node.parent; - // There are 3 cases to handle: - // 1. The token introduces a list, and should begin a sig help session - // 2. The token is either not associated with a list, or ends a list, so the session should end - // 3. The token is buried inside a list, and should give sig help - // - // The following are examples of each: - // - // Case 1: - // foo<#T, U>(#a, b) -> The token introduces a list, and should begin a sig help session - // Case 2: - // fo#o#(a, b)# -> The token is either not associated with a list, or ends a list, so the session should end - // Case 3: - // foo(a#, #b#) -> The token is buried inside a list, and should give sig help - // Find out if 'node' is an argument, a type argument, or neither - if (node.kind === SyntaxKind.LessThanToken || - node.kind === SyntaxKind.OpenParenToken) { - // Find the list that starts right *after* the < or ( token. - // If the user has just opened a list, consider this item 0. - const list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile); - const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; - Debug.assert(list !== undefined); - return { - kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, - invocation: callExpression, - argumentsSpan: getApplicableSpanForArguments(list), - argumentIndex: 0, - argumentCount: getArgumentCount(list) - }; - } - - // findListItemInfo can return undefined if we are not in parent's argument list - // or type argument list. This includes cases where the cursor is: - // - To the right of the closing paren, non-substitution template, or template tail. - // - Between the type arguments and the arguments (greater than token) - // - On the target of the call (parent.func) - // - On the 'new' keyword in a 'new' expression - const listItemInfo = findListItemInfo(node); - if (listItemInfo) { - const list = listItemInfo.list; - const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; - - const argumentIndex = getArgumentIndex(list, node); - const argumentCount = getArgumentCount(list); - - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, - `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); - - return { - kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, - invocation: callExpression, - argumentsSpan: getApplicableSpanForArguments(list), - argumentIndex: argumentIndex, - argumentCount: argumentCount - }; - } - } - else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && node.parent.kind === SyntaxKind.TaggedTemplateExpression) { - // Check if we're actually inside the template; - // otherwise we'll fall out and return undefined. - if (isInsideTemplateLiteral(node, position)) { - return getArgumentListInfoForTemplate(node.parent, /*argumentIndex*/ 0); - } - } - else if (node.kind === SyntaxKind.TemplateHead && node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { - const templateExpression = node.parent; - const tagExpression = templateExpression.parent; - Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); - - const argumentIndex = isInsideTemplateLiteral(node, position) ? 0 : 1; - - return getArgumentListInfoForTemplate(tagExpression, argumentIndex); - } - else if (node.parent.kind === SyntaxKind.TemplateSpan && node.parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { - const templateSpan = node.parent; - const templateExpression = templateSpan.parent; - const tagExpression = templateExpression.parent; - Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); - - // If we're just after a template tail, don't show signature help. - if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(node, position)) { - return undefined; - } - - const spanIndex = templateExpression.templateSpans.indexOf(templateSpan); - const argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node); - - return getArgumentListInfoForTemplate(tagExpression, argumentIndex); - } - - return undefined; - } - - function getArgumentIndex(argumentsList: Node, node: Node) { - // The list we got back can include commas. In the presence of errors it may - // also just have nodes without commas. For example "Foo(a b c)" will have 3 - // args without commas. We want to find what index we're at. So we count - // forward until we hit ourselves, only incrementing the index if it isn't a - // comma. + /** + * Returns relevant information for the argument list and the current argument if we are + * in the argument of an invocation; returns undefined otherwise. + */ + function getImmediatelyContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo { + if (node.parent.kind === SyntaxKind.CallExpression || node.parent.kind === SyntaxKind.NewExpression) { + const callExpression = node.parent; + // There are 3 cases to handle: + // 1. The token introduces a list, and should begin a sig help session + // 2. The token is either not associated with a list, or ends a list, so the session should end + // 3. The token is buried inside a list, and should give sig help // - // Note: the subtlety around trailing commas (in getArgumentCount) does not apply - // here. That's because we're only walking forward until we hit the node we're - // on. In that case, even if we're after the trailing comma, we'll still see - // that trailing comma in the list, and we'll have generated the appropriate - // arg index. - let argumentIndex = 0; - const listChildren = argumentsList.getChildren(); - for (const child of listChildren) { - if (child === node) { - break; - } - if (child.kind !== SyntaxKind.CommaToken) { - argumentIndex++; - } + // The following are examples of each: + // + // Case 1: + // foo<#T, U>(#a, b) -> The token introduces a list, and should begin a sig help session + // Case 2: + // fo#o#(a, b)# -> The token is either not associated with a list, or ends a list, so the session should end + // Case 3: + // foo(a#, #b#) -> The token is buried inside a list, and should give sig help + // Find out if 'node' is an argument, a type argument, or neither + if (node.kind === SyntaxKind.LessThanToken || + node.kind === SyntaxKind.OpenParenToken) { + // Find the list that starts right *after* the < or ( token. + // If the user has just opened a list, consider this item 0. + const list = getChildListThatStartsWithOpenerToken(callExpression, node, sourceFile); + const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; + Debug.assert(list !== undefined); + return { + kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, + invocation: callExpression, + argumentsSpan: getApplicableSpanForArguments(list, sourceFile), + argumentIndex: 0, + argumentCount: getArgumentCount(list) + }; } - return argumentIndex; - } + // findListItemInfo can return undefined if we are not in parent's argument list + // or type argument list. This includes cases where the cursor is: + // - To the right of the closing paren, non-substitution template, or template tail. + // - Between the type arguments and the arguments (greater than token) + // - On the target of the call (parent.func) + // - On the 'new' keyword in a 'new' expression + const listItemInfo = findListItemInfo(node); + if (listItemInfo) { + const list = listItemInfo.list; + const isTypeArgList = callExpression.typeArguments && callExpression.typeArguments.pos === list.pos; - function getArgumentCount(argumentsList: Node) { - // The argument count for a list is normally the number of non-comma children it has. - // For example, if you have "Foo(a,b)" then there will be three children of the arg - // list 'a' '' 'b'. So, in this case the arg count will be 2. However, there - // is a small subtlety. If you have "Foo(a,)", then the child list will just have - // 'a' ''. So, in the case where the last child is a comma, we increase the - // arg count by one to compensate. - // - // Note: this subtlety only applies to the last comma. If you had "Foo(a,," then - // we'll have: 'a' '' '' - // That will give us 2 non-commas. We then add one for the last comma, givin us an - // arg count of 3. - const listChildren = argumentsList.getChildren(); + const argumentIndex = getArgumentIndex(list, node); + const argumentCount = getArgumentCount(list); - let argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken); - if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) { - argumentCount++; - } + Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, + `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); - return argumentCount; - } - - // spanIndex is either the index for a given template span. - // This does not give appropriate results for a NoSubstitutionTemplateLiteral - function getArgumentIndexForTemplatePiece(spanIndex: number, node: Node): number { - // Because the TemplateStringsArray is the first argument, we have to offset each substitution expression by 1. - // There are three cases we can encounter: - // 1. We are precisely in the template literal (argIndex = 0). - // 2. We are in or to the right of the substitution expression (argIndex = spanIndex + 1). - // 3. We are directly to the right of the template literal, but because we look for the token on the left, - // not enough to put us in the substitution expression; we should consider ourselves part of - // the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1). - // - // Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # ` - // ^ ^ ^ ^ ^ ^ ^ ^ ^ - // Case: 1 1 3 2 1 3 2 2 1 - Debug.assert(position >= node.getStart(), "Assumed 'position' could not occur before node."); - if (isTemplateLiteralKind(node.kind)) { - if (isInsideTemplateLiteral(node, position)) { - return 0; - } - return spanIndex + 2; - } - return spanIndex + 1; - } - - function getArgumentListInfoForTemplate(tagExpression: TaggedTemplateExpression, argumentIndex: number): ArgumentListInfo { - // argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument. - const argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral - ? 1 - : (tagExpression.template).templateSpans.length + 1; - - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); - - return { - kind: ArgumentListKind.TaggedTemplateArguments, - invocation: tagExpression, - argumentsSpan: getApplicableSpanForTaggedTemplate(tagExpression), - argumentIndex: argumentIndex, - argumentCount: argumentCount - }; - } - - function getApplicableSpanForArguments(argumentsList: Node): TextSpan { - // We use full start and skip trivia on the end because we want to include trivia on - // both sides. For example, - // - // foo( /*comment */ a, b, c /*comment*/ ) - // | | - // - // The applicable span is from the first bar to the second bar (inclusive, - // but not including parentheses) - const applicableSpanStart = argumentsList.getFullStart(); - const applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false); - return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); - } - - function getApplicableSpanForTaggedTemplate(taggedTemplate: TaggedTemplateExpression): TextSpan { - const template = taggedTemplate.template; - const applicableSpanStart = template.getStart(); - let applicableSpanEnd = template.getEnd(); - - // We need to adjust the end position for the case where the template does not have a tail. - // Otherwise, we will not show signature help past the expression. - // For example, - // - // ` ${ 1 + 1 foo(10) - // | | - // - // This is because a Missing node has no width. However, what we actually want is to include trivia - // leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail. - if (template.kind === SyntaxKind.TemplateExpression) { - const lastSpan = lastOrUndefined((template).templateSpans); - if (lastSpan.literal.getFullWidth() === 0) { - applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false); - } - } - - return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); - } - - function getContainingArgumentInfo(node: Node): ArgumentListInfo { - for (let n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) { - if (isFunctionBlock(n)) { - return undefined; - } - - // If the node is not a subspan of its parent, this is a big problem. - // There have been crashes that might be caused by this violation. - if (n.pos < n.parent.pos || n.end > n.parent.end) { - Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind); - } - - const argumentInfo = getImmediatelyContainingArgumentInfo(n); - if (argumentInfo) { - return argumentInfo; - } - - - // TODO: Handle generic call with incomplete syntax + return { + kind: isTypeArgList ? ArgumentListKind.TypeArguments : ArgumentListKind.CallArguments, + invocation: callExpression, + argumentsSpan: getApplicableSpanForArguments(list, sourceFile), + argumentIndex: argumentIndex, + argumentCount: argumentCount + }; } return undefined; } - - function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node { - const children = parent.getChildren(sourceFile); - const indexOfOpenerToken = children.indexOf(openerToken); - Debug.assert(indexOfOpenerToken >= 0 && children.length > indexOfOpenerToken + 1); - return children[indexOfOpenerToken + 1]; + else if (node.kind === SyntaxKind.NoSubstitutionTemplateLiteral && node.parent.kind === SyntaxKind.TaggedTemplateExpression) { + // Check if we're actually inside the template; + // otherwise we'll fall out and return undefined. + if (isInsideTemplateLiteral(node, position)) { + return getArgumentListInfoForTemplate(node.parent, /*argumentIndex*/ 0, sourceFile); + } } + else if (node.kind === SyntaxKind.TemplateHead && node.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { + const templateExpression = node.parent; + const tagExpression = templateExpression.parent; + Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); - /** - * The selectedItemIndex could be negative for several reasons. - * 1. There are too many arguments for all of the overloads - * 2. None of the overloads were type compatible - * The solution here is to try to pick the best overload by picking - * either the first one that has an appropriate number of parameters, - * or the one with the most parameters. - */ - function selectBestInvalidOverloadIndex(candidates: Signature[], argumentCount: number): number { - let maxParamsSignatureIndex = -1; - let maxParams = -1; - for (let i = 0; i < candidates.length; i++) { - const candidate = candidates[i]; + const argumentIndex = isInsideTemplateLiteral(node, position) ? 0 : 1; - if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) { - return i; - } + return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile); + } + else if (node.parent.kind === SyntaxKind.TemplateSpan && node.parent.parent.parent.kind === SyntaxKind.TaggedTemplateExpression) { + const templateSpan = node.parent; + const templateExpression = templateSpan.parent; + const tagExpression = templateExpression.parent; + Debug.assert(templateExpression.kind === SyntaxKind.TemplateExpression); - if (candidate.parameters.length > maxParams) { - maxParams = candidate.parameters.length; - maxParamsSignatureIndex = i; - } + // If we're just after a template tail, don't show signature help. + if (node.kind === SyntaxKind.TemplateTail && !isInsideTemplateLiteral(node, position)) { + return undefined; } - return maxParamsSignatureIndex; + const spanIndex = templateExpression.templateSpans.indexOf(templateSpan); + const argumentIndex = getArgumentIndexForTemplatePiece(spanIndex, node, position); + + return getArgumentListInfoForTemplate(tagExpression, argumentIndex, sourceFile); } - function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo): SignatureHelpItems { - const applicableSpan = argumentListInfo.argumentsSpan; - const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments; + return undefined; + } - const invocation = argumentListInfo.invocation; - const callTarget = getInvokedExpression(invocation); - const callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget); - const callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); - const items: SignatureHelpItem[] = map(candidates, candidateSignature => { - let signatureHelpParameters: SignatureHelpParameter[]; - const prefixDisplayParts: SymbolDisplayPart[] = []; - const suffixDisplayParts: SymbolDisplayPart[] = []; + function getArgumentIndex(argumentsList: Node, node: Node) { + // The list we got back can include commas. In the presence of errors it may + // also just have nodes without commas. For example "Foo(a b c)" will have 3 + // args without commas. We want to find what index we're at. So we count + // forward until we hit ourselves, only incrementing the index if it isn't a + // comma. + // + // Note: the subtlety around trailing commas (in getArgumentCount) does not apply + // here. That's because we're only walking forward until we hit the node we're + // on. In that case, even if we're after the trailing comma, we'll still see + // that trailing comma in the list, and we'll have generated the appropriate + // arg index. + let argumentIndex = 0; + const listChildren = argumentsList.getChildren(); + for (const child of listChildren) { + if (child === node) { + break; + } + if (child.kind !== SyntaxKind.CommaToken) { + argumentIndex++; + } + } - if (callTargetDisplayParts) { - addRange(prefixDisplayParts, callTargetDisplayParts); - } + return argumentIndex; + } - if (isTypeParameterList) { - prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken)); - const typeParameters = candidateSignature.typeParameters; - signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; - suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken)); - const parameterParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation)); - addRange(suffixDisplayParts, parameterParts); - } - else { - const typeParameterParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation)); - addRange(prefixDisplayParts, typeParameterParts); - prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); + function getArgumentCount(argumentsList: Node) { + // The argument count for a list is normally the number of non-comma children it has. + // For example, if you have "Foo(a,b)" then there will be three children of the arg + // list 'a' '' 'b'. So, in this case the arg count will be 2. However, there + // is a small subtlety. If you have "Foo(a,)", then the child list will just have + // 'a' ''. So, in the case where the last child is a comma, we increase the + // arg count by one to compensate. + // + // Note: this subtlety only applies to the last comma. If you had "Foo(a,," then + // we'll have: 'a' '' '' + // That will give us 2 non-commas. We then add one for the last comma, givin us an + // arg count of 3. + const listChildren = argumentsList.getChildren(); - const parameters = candidateSignature.parameters; - signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray; - suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); - } + let argumentCount = countWhere(listChildren, arg => arg.kind !== SyntaxKind.CommaToken); + if (listChildren.length > 0 && lastOrUndefined(listChildren).kind === SyntaxKind.CommaToken) { + argumentCount++; + } - const returnTypeParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildReturnTypeDisplay(candidateSignature, writer, invocation)); - addRange(suffixDisplayParts, returnTypeParts); + return argumentCount; + } - return { - isVariadic: candidateSignature.hasRestParameter, - prefixDisplayParts, - suffixDisplayParts, - separatorDisplayParts: [punctuationPart(SyntaxKind.CommaToken), spacePart()], - parameters: signatureHelpParameters, - documentation: candidateSignature.getDocumentationComment() - }; - }); + // spanIndex is either the index for a given template span. + // This does not give appropriate results for a NoSubstitutionTemplateLiteral + function getArgumentIndexForTemplatePiece(spanIndex: number, node: Node, position: number): number { + // Because the TemplateStringsArray is the first argument, we have to offset each substitution expression by 1. + // There are three cases we can encounter: + // 1. We are precisely in the template literal (argIndex = 0). + // 2. We are in or to the right of the substitution expression (argIndex = spanIndex + 1). + // 3. We are directly to the right of the template literal, but because we look for the token on the left, + // not enough to put us in the substitution expression; we should consider ourselves part of + // the *next* span's expression by offsetting the index (argIndex = (spanIndex + 1) + 1). + // + // Example: f `# abcd $#{# 1 + 1# }# efghi ${ #"#hello"# } # ` + // ^ ^ ^ ^ ^ ^ ^ ^ ^ + // Case: 1 1 3 2 1 3 2 2 1 + Debug.assert(position >= node.getStart(), "Assumed 'position' could not occur before node."); + if (isTemplateLiteralKind(node.kind)) { + if (isInsideTemplateLiteral(node, position)) { + return 0; + } + return spanIndex + 2; + } + return spanIndex + 1; + } - const argumentIndex = argumentListInfo.argumentIndex; + function getArgumentListInfoForTemplate(tagExpression: TaggedTemplateExpression, argumentIndex: number, sourceFile: SourceFile): ArgumentListInfo { + // argumentCount is either 1 or (numSpans + 1) to account for the template strings array argument. + const argumentCount = tagExpression.template.kind === SyntaxKind.NoSubstitutionTemplateLiteral + ? 1 + : (tagExpression.template).templateSpans.length + 1; - // argumentCount is the *apparent* number of arguments. - const argumentCount = argumentListInfo.argumentCount; + Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); - let selectedItemIndex = candidates.indexOf(bestSignature); - if (selectedItemIndex < 0) { - selectedItemIndex = selectBestInvalidOverloadIndex(candidates, argumentCount); + return { + kind: ArgumentListKind.TaggedTemplateArguments, + invocation: tagExpression, + argumentsSpan: getApplicableSpanForTaggedTemplate(tagExpression, sourceFile), + argumentIndex: argumentIndex, + argumentCount: argumentCount + }; + } + + function getApplicableSpanForArguments(argumentsList: Node, sourceFile: SourceFile): TextSpan { + // We use full start and skip trivia on the end because we want to include trivia on + // both sides. For example, + // + // foo( /*comment */ a, b, c /*comment*/ ) + // | | + // + // The applicable span is from the first bar to the second bar (inclusive, + // but not including parentheses) + const applicableSpanStart = argumentsList.getFullStart(); + const applicableSpanEnd = skipTrivia(sourceFile.text, argumentsList.getEnd(), /*stopAfterLineBreak*/ false); + return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); + } + + function getApplicableSpanForTaggedTemplate(taggedTemplate: TaggedTemplateExpression, sourceFile: SourceFile): TextSpan { + const template = taggedTemplate.template; + const applicableSpanStart = template.getStart(); + let applicableSpanEnd = template.getEnd(); + + // We need to adjust the end position for the case where the template does not have a tail. + // Otherwise, we will not show signature help past the expression. + // For example, + // + // ` ${ 1 + 1 foo(10) + // | | + // + // This is because a Missing node has no width. However, what we actually want is to include trivia + // leading up to the next token in case the user is about to type in a TemplateMiddle or TemplateTail. + if (template.kind === SyntaxKind.TemplateExpression) { + const lastSpan = lastOrUndefined((template).templateSpans); + if (lastSpan.literal.getFullWidth() === 0) { + applicableSpanEnd = skipTrivia(sourceFile.text, applicableSpanEnd, /*stopAfterLineBreak*/ false); + } + } + + return createTextSpan(applicableSpanStart, applicableSpanEnd - applicableSpanStart); + } + + export function getContainingArgumentInfo(node: Node, position: number, sourceFile: SourceFile): ArgumentListInfo { + for (let n = node; n.kind !== SyntaxKind.SourceFile; n = n.parent) { + if (isFunctionBlock(n)) { + return undefined; } - Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + // If the node is not a subspan of its parent, this is a big problem. + // There have been crashes that might be caused by this violation. + if (n.pos < n.parent.pos || n.end > n.parent.end) { + Debug.fail("Node of kind " + n.kind + " is not a subspan of its parent of kind " + n.parent.kind); + } + + const argumentInfo = getImmediatelyContainingArgumentInfo(n, position, sourceFile); + if (argumentInfo) { + return argumentInfo; + } + + + // TODO: Handle generic call with incomplete syntax + } + return undefined; + } + + function getChildListThatStartsWithOpenerToken(parent: Node, openerToken: Node, sourceFile: SourceFile): Node { + const children = parent.getChildren(sourceFile); + const indexOfOpenerToken = children.indexOf(openerToken); + Debug.assert(indexOfOpenerToken >= 0 && children.length > indexOfOpenerToken + 1); + return children[indexOfOpenerToken + 1]; + } + + /** + * The selectedItemIndex could be negative for several reasons. + * 1. There are too many arguments for all of the overloads + * 2. None of the overloads were type compatible + * The solution here is to try to pick the best overload by picking + * either the first one that has an appropriate number of parameters, + * or the one with the most parameters. + */ + function selectBestInvalidOverloadIndex(candidates: Signature[], argumentCount: number): number { + let maxParamsSignatureIndex = -1; + let maxParams = -1; + for (let i = 0; i < candidates.length; i++) { + const candidate = candidates[i]; + + if (candidate.hasRestParameter || candidate.parameters.length >= argumentCount) { + return i; + } + + if (candidate.parameters.length > maxParams) { + maxParams = candidate.parameters.length; + maxParamsSignatureIndex = i; + } + } + + return maxParamsSignatureIndex; + } + + function createSignatureHelpItems(candidates: Signature[], bestSignature: Signature, argumentListInfo: ArgumentListInfo, typeChecker: TypeChecker): SignatureHelpItems { + const applicableSpan = argumentListInfo.argumentsSpan; + const isTypeParameterList = argumentListInfo.kind === ArgumentListKind.TypeArguments; + + const invocation = argumentListInfo.invocation; + const callTarget = getInvokedExpression(invocation); + const callTargetSymbol = typeChecker.getSymbolAtLocation(callTarget); + const callTargetDisplayParts = callTargetSymbol && symbolToDisplayParts(typeChecker, callTargetSymbol, /*enclosingDeclaration*/ undefined, /*meaning*/ undefined); + const items: SignatureHelpItem[] = map(candidates, candidateSignature => { + let signatureHelpParameters: SignatureHelpParameter[]; + const prefixDisplayParts: SymbolDisplayPart[] = []; + const suffixDisplayParts: SymbolDisplayPart[] = []; + + if (callTargetDisplayParts) { + addRange(prefixDisplayParts, callTargetDisplayParts); + } + + if (isTypeParameterList) { + prefixDisplayParts.push(punctuationPart(SyntaxKind.LessThanToken)); + const typeParameters = candidateSignature.typeParameters; + signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; + suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken)); + const parameterParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation)); + addRange(suffixDisplayParts, parameterParts); + } + else { + const typeParameterParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildDisplayForTypeParametersAndDelimiters(candidateSignature.typeParameters, writer, invocation)); + addRange(prefixDisplayParts, typeParameterParts); + prefixDisplayParts.push(punctuationPart(SyntaxKind.OpenParenToken)); + + const parameters = candidateSignature.parameters; + signatureHelpParameters = parameters.length > 0 ? map(parameters, createSignatureHelpParameterForParameter) : emptyArray; + suffixDisplayParts.push(punctuationPart(SyntaxKind.CloseParenToken)); + } + + const returnTypeParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildReturnTypeDisplay(candidateSignature, writer, invocation)); + addRange(suffixDisplayParts, returnTypeParts); return { - items, - applicableSpan, - selectedItemIndex, - argumentIndex, - argumentCount + isVariadic: candidateSignature.hasRestParameter, + prefixDisplayParts, + suffixDisplayParts, + separatorDisplayParts: [punctuationPart(SyntaxKind.CommaToken), spacePart()], + parameters: signatureHelpParameters, + documentation: candidateSignature.getDocumentationComment() }; + }); - function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter { - const displayParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation)); + const argumentIndex = argumentListInfo.argumentIndex; - return { - name: parameter.name, - documentation: parameter.getDocumentationComment(), - displayParts, - isOptional: typeChecker.isOptionalParameter(parameter.valueDeclaration) - }; - } + // argumentCount is the *apparent* number of arguments. + const argumentCount = argumentListInfo.argumentCount; - function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter { - const displayParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation)); + let selectedItemIndex = candidates.indexOf(bestSignature); + if (selectedItemIndex < 0) { + selectedItemIndex = selectBestInvalidOverloadIndex(candidates, argumentCount); + } - return { - name: typeParameter.symbol.name, - documentation: emptyArray, - displayParts, - isOptional: false - }; - } + Debug.assert(argumentIndex === 0 || argumentIndex < argumentCount, `argumentCount < argumentIndex, ${argumentCount} < ${argumentIndex}`); + + return { + items, + applicableSpan, + selectedItemIndex, + argumentIndex, + argumentCount + }; + + function createSignatureHelpParameterForParameter(parameter: Symbol): SignatureHelpParameter { + const displayParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildParameterDisplay(parameter, writer, invocation)); + + return { + name: parameter.name, + documentation: parameter.getDocumentationComment(), + displayParts, + isOptional: typeChecker.isOptionalParameter(parameter.valueDeclaration) + }; + } + + function createSignatureHelpParameterForTypeParameter(typeParameter: TypeParameter): SignatureHelpParameter { + const displayParts = mapToDisplayParts(writer => + typeChecker.getSymbolDisplayBuilder().buildTypeParameterDisplay(typeParameter, writer, invocation)); + + return { + name: typeParameter.symbol.name, + documentation: emptyArray, + displayParts, + isOptional: false + }; } } } \ No newline at end of file diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 54372877b9a..d49fa390435 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -426,9 +426,27 @@ namespace ts { } } - export function isInString(sourceFile: SourceFile, position: number) { - const token = getTokenAtPosition(sourceFile, position); - return token && (token.kind === SyntaxKind.StringLiteral || token.kind === SyntaxKind.StringLiteralType) && position > token.getStart(sourceFile); + export function isInString(sourceFile: SourceFile, position: number): boolean { + const previousToken = findPrecedingToken(position, sourceFile); + if (previousToken && + (previousToken.kind === SyntaxKind.StringLiteral || previousToken.kind === SyntaxKind.StringLiteralType)) { + const start = previousToken.getStart(); + const end = previousToken.getEnd(); + + // To be "in" one of these literals, the position has to be: + // 1. entirely within the token text. + // 2. at the end position of an unterminated token. + // 3. at the end of a regular expression (due to trailing flags like '/foo/g'). + if (start < position && position < end) { + return true; + } + + if (position === end) { + return !!(previousToken).isUnterminated; + } + } + + return false; } export function isInComment(sourceFile: SourceFile, position: number) { diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.js b/tests/baselines/reference/ambientDeclarationsPatterns.js new file mode 100644 index 00000000000..143f26550e9 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.js @@ -0,0 +1,44 @@ +//// [tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts] //// + +//// [declarations.d.ts] +declare module "foo*baz" { + export function foo(s: string): void; +} +// Augmentations still work +declare module "foo*baz" { + export const baz: string; +} + +// Longest prefix wins +declare module "foos*" { + export const foos: string; +} + +declare module "*!text" { + const x: string; + export default x; +} + +//// [user.ts] +/// +import {foo, baz} from "foobarbaz"; +foo(baz); + +import {foos} from "foosball"; +foo(foos); + +// Works with relative file name +import fileText from "./file!text"; +foo(fileText); + + +//// [user.js] +"use strict"; +/// +var foobarbaz_1 = require("foobarbaz"); +foobarbaz_1.foo(foobarbaz_1.baz); +var foosball_1 = require("foosball"); +foobarbaz_1.foo(foosball_1.foos); +// Works with relative file name +var file_text_1 = require("./file!text"); +foobarbaz_1.foo(file_text_1["default"]); diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.symbols b/tests/baselines/reference/ambientDeclarationsPatterns.symbols new file mode 100644 index 00000000000..4c0acc93f8f --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.symbols @@ -0,0 +1,51 @@ +=== tests/cases/conformance/ambient/user.ts === +/// +import {foo, baz} from "foobarbaz"; +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>baz : Symbol(baz, Decl(user.ts, 1, 12)) + +foo(baz); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>baz : Symbol(baz, Decl(user.ts, 1, 12)) + +import {foos} from "foosball"; +>foos : Symbol(foos, Decl(user.ts, 4, 8)) + +foo(foos); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>foos : Symbol(foos, Decl(user.ts, 4, 8)) + +// Works with relative file name +import fileText from "./file!text"; +>fileText : Symbol(fileText, Decl(user.ts, 8, 6)) + +foo(fileText); +>foo : Symbol(foo, Decl(user.ts, 1, 8)) +>fileText : Symbol(fileText, Decl(user.ts, 8, 6)) + +=== tests/cases/conformance/ambient/declarations.d.ts === +declare module "foo*baz" { + export function foo(s: string): void; +>foo : Symbol(foo, Decl(declarations.d.ts, 0, 26)) +>s : Symbol(s, Decl(declarations.d.ts, 1, 24)) +} +// Augmentations still work +declare module "foo*baz" { + export const baz: string; +>baz : Symbol(baz, Decl(declarations.d.ts, 5, 16)) +} + +// Longest prefix wins +declare module "foos*" { + export const foos: string; +>foos : Symbol(foos, Decl(declarations.d.ts, 10, 16)) +} + +declare module "*!text" { + const x: string; +>x : Symbol(x, Decl(declarations.d.ts, 14, 9)) + + export default x; +>x : Symbol(x, Decl(declarations.d.ts, 14, 9)) +} + diff --git a/tests/baselines/reference/ambientDeclarationsPatterns.types b/tests/baselines/reference/ambientDeclarationsPatterns.types new file mode 100644 index 00000000000..adf8ae1ab3b --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns.types @@ -0,0 +1,54 @@ +=== tests/cases/conformance/ambient/user.ts === +/// +import {foo, baz} from "foobarbaz"; +>foo : (s: string) => void +>baz : string + +foo(baz); +>foo(baz) : void +>foo : (s: string) => void +>baz : string + +import {foos} from "foosball"; +>foos : string + +foo(foos); +>foo(foos) : void +>foo : (s: string) => void +>foos : string + +// Works with relative file name +import fileText from "./file!text"; +>fileText : string + +foo(fileText); +>foo(fileText) : void +>foo : (s: string) => void +>fileText : string + +=== tests/cases/conformance/ambient/declarations.d.ts === +declare module "foo*baz" { + export function foo(s: string): void; +>foo : (s: string) => void +>s : string +} +// Augmentations still work +declare module "foo*baz" { + export const baz: string; +>baz : string +} + +// Longest prefix wins +declare module "foos*" { + export const foos: string; +>foos : string +} + +declare module "*!text" { + const x: string; +>x : string + + export default x; +>x : string +} + diff --git a/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt new file mode 100644 index 00000000000..7a3ff02aa5e --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.errors.txt @@ -0,0 +1,8 @@ +tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts(1,16): error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + + +==== tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts (1 errors) ==== + declare module "too*many*asterisks" { } + ~~~~~~~~~~~~~~~~~~~~ +!!! error TS5061: Pattern 'too*many*asterisks' can have at most one '*' character + \ No newline at end of file diff --git a/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js new file mode 100644 index 00000000000..a664eb84dc5 --- /dev/null +++ b/tests/baselines/reference/ambientDeclarationsPatterns_tooManyAsterisks.js @@ -0,0 +1,5 @@ +//// [ambientDeclarationsPatterns_tooManyAsterisks.ts] +declare module "too*many*asterisks" { } + + +//// [ambientDeclarationsPatterns_tooManyAsterisks.js] diff --git a/tests/baselines/reference/asyncFunctionReturnType.js b/tests/baselines/reference/asyncFunctionReturnType.js new file mode 100644 index 00000000000..12cb44fbcf0 --- /dev/null +++ b/tests/baselines/reference/asyncFunctionReturnType.js @@ -0,0 +1,33 @@ +//// [asyncFunctionReturnType.ts] +async function fAsync() { + // Without explicit type annotation, this is just an array. + return [1, true]; +} + +async function fAsyncExplicit(): Promise<[number, boolean]> { + // This is contextually typed as a tuple. + return [1, true]; +} + + +//// [asyncFunctionReturnType.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function fAsync() { + return __awaiter(this, void 0, void 0, function* () { + // Without explicit type annotation, this is just an array. + return [1, true]; + }); +} +function fAsyncExplicit() { + return __awaiter(this, void 0, void 0, function* () { + // This is contextually typed as a tuple. + return [1, true]; + }); +} diff --git a/tests/baselines/reference/asyncFunctionReturnType.symbols b/tests/baselines/reference/asyncFunctionReturnType.symbols new file mode 100644 index 00000000000..75e456b6a8b --- /dev/null +++ b/tests/baselines/reference/asyncFunctionReturnType.symbols @@ -0,0 +1,16 @@ +=== tests/cases/compiler/asyncFunctionReturnType.ts === +async function fAsync() { +>fAsync : Symbol(fAsync, Decl(asyncFunctionReturnType.ts, 0, 0)) + + // Without explicit type annotation, this is just an array. + return [1, true]; +} + +async function fAsyncExplicit(): Promise<[number, boolean]> { +>fAsyncExplicit : Symbol(fAsyncExplicit, Decl(asyncFunctionReturnType.ts, 3, 1)) +>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, --, --)) + + // This is contextually typed as a tuple. + return [1, true]; +} + diff --git a/tests/baselines/reference/asyncFunctionReturnType.types b/tests/baselines/reference/asyncFunctionReturnType.types new file mode 100644 index 00000000000..c6289ab012d --- /dev/null +++ b/tests/baselines/reference/asyncFunctionReturnType.types @@ -0,0 +1,22 @@ +=== tests/cases/compiler/asyncFunctionReturnType.ts === +async function fAsync() { +>fAsync : () => Promise<(number | boolean)[]> + + // Without explicit type annotation, this is just an array. + return [1, true]; +>[1, true] : (number | boolean)[] +>1 : number +>true : boolean +} + +async function fAsyncExplicit(): Promise<[number, boolean]> { +>fAsyncExplicit : () => Promise<[number, boolean]> +>Promise : Promise + + // This is contextually typed as a tuple. + return [1, true]; +>[1, true] : [number, boolean] +>1 : number +>true : boolean +} + diff --git a/tests/baselines/reference/disallowAsyncModifierInES5.errors.txt b/tests/baselines/reference/disallowAsyncModifierInES5.errors.txt new file mode 100644 index 00000000000..655416f784c --- /dev/null +++ b/tests/baselines/reference/disallowAsyncModifierInES5.errors.txt @@ -0,0 +1,27 @@ +error TS2318: Cannot find global type 'Promise'. +tests/cases/compiler/disallowAsyncModifierInES5.ts(2,1): error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. +tests/cases/compiler/disallowAsyncModifierInES5.ts(2,16): error TS1057: An async function or method must have a valid awaitable return type. +tests/cases/compiler/disallowAsyncModifierInES5.ts(3,11): error TS1057: An async function or method must have a valid awaitable return type. +tests/cases/compiler/disallowAsyncModifierInES5.ts(3,11): error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. +tests/cases/compiler/disallowAsyncModifierInES5.ts(4,11): error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. +tests/cases/compiler/disallowAsyncModifierInES5.ts(4,11): error TS1057: An async function or method must have a valid awaitable return type. + + +!!! error TS2318: Cannot find global type 'Promise'. +==== tests/cases/compiler/disallowAsyncModifierInES5.ts (6 errors) ==== + + async function foo() { return 42; } // ERROR: Async functions are only available in ES6+ + ~~~~~ +!!! error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. + ~~~ +!!! error TS1057: An async function or method must have a valid awaitable return type. + let bar = async function () { return 42; } // OK, but should be an error + ~~~~~ +!!! error TS1057: An async function or method must have a valid awaitable return type. + ~~~~~ +!!! error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. + let baz = async () => 42; // OK, but should be an error + ~~~~~ +!!! error TS1311: Async functions are only available when targeting ECMAScript 6 and higher. + ~~~~~~~~~~~~~~ +!!! error TS1057: An async function or method must have a valid awaitable return type. \ No newline at end of file diff --git a/tests/baselines/reference/disallowAsyncModifierInES5.js b/tests/baselines/reference/disallowAsyncModifierInES5.js new file mode 100644 index 00000000000..8216e5fd0e6 --- /dev/null +++ b/tests/baselines/reference/disallowAsyncModifierInES5.js @@ -0,0 +1,22 @@ +//// [disallowAsyncModifierInES5.ts] + +async function foo() { return 42; } // ERROR: Async functions are only available in ES6+ +let bar = async function () { return 42; } // OK, but should be an error +let baz = async () => 42; // OK, but should be an error + +//// [disallowAsyncModifierInES5.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments)).next()); + }); +}; +function foo() { + return __awaiter(this, void 0, void 0, function* () { return 42; }); +} // ERROR: Async functions are only available in ES6+ +var bar = function () { + return __awaiter(this, void 0, void 0, function* () { return 42; }); +}; // OK, but should be an error +var baz = function () __awaiter(this, void 0, void 0, function* () { return 42; }); // OK, but should be an error diff --git a/tests/baselines/reference/instanceOfAssignability.types b/tests/baselines/reference/instanceOfAssignability.types index 70d068fb0cc..44ec45e2069 100644 --- a/tests/baselines/reference/instanceOfAssignability.types +++ b/tests/baselines/reference/instanceOfAssignability.types @@ -133,8 +133,8 @@ function fn5(x: Derived1) { // 1.5: y: Derived1 // Want: ??? let y = x; ->y : never ->x : never +>y : Derived1 & Derived2 +>x : Derived1 & Derived2 } } diff --git a/tests/baselines/reference/logicalAndOperatorStrictMode.js b/tests/baselines/reference/logicalAndOperatorStrictMode.js new file mode 100644 index 00000000000..ecb5bc16429 --- /dev/null +++ b/tests/baselines/reference/logicalAndOperatorStrictMode.js @@ -0,0 +1,156 @@ +//// [logicalAndOperatorStrictMode.ts] + +const a = [0]; +const s = ""; +const x = 0; +const b = false; +const v: void = undefined; +const u = undefined; +const n = null; +const z = s || x || u; + +const a1 = a && a; +const a2 = a && s; +const a3 = a && x; +const a4 = a && b; +const a5 = a && v; +const a6 = a && u; +const a7 = a && n; +const a8 = a && z; + +const s1 = s && a; +const s2 = s && s; +const s3 = s && x; +const s4 = s && b; +const s5 = s && v; +const s6 = s && u; +const s7 = s && n; +const s8 = s && z; + +const x1 = x && a; +const x2 = x && s; +const x3 = x && x; +const x4 = x && b; +const x5 = x && v; +const x6 = x && u; +const x7 = x && n; +const x8 = x && z; + +const b1 = b && a; +const b2 = b && s; +const b3 = b && x; +const b4 = b && b; +const b5 = b && v; +const b6 = b && u; +const b7 = b && n; +const b8 = b && z; + +const v1 = v && a; +const v2 = v && s; +const v3 = v && x; +const v4 = v && b; +const v5 = v && v; +const v6 = v && u; +const v7 = v && n; +const v8 = v && z; + +const u1 = u && a; +const u2 = u && s; +const u3 = u && x; +const u4 = u && b; +const u5 = u && v; +const u6 = u && u; +const u7 = u && n; +const u8 = u && z; + +const n1 = n && a; +const n2 = n && s; +const n3 = n && x; +const n4 = n && b; +const n5 = n && v; +const n6 = n && u; +const n7 = n && n; +const n8 = n && z; + +const z1 = z && a; +const z2 = z && s; +const z3 = z && x; +const z4 = z && b; +const z5 = z && v; +const z6 = z && u; +const z7 = z && n; +const z8 = z && z; + +//// [logicalAndOperatorStrictMode.js] +var a = [0]; +var s = ""; +var x = 0; +var b = false; +var v = undefined; +var u = undefined; +var n = null; +var z = s || x || u; +var a1 = a && a; +var a2 = a && s; +var a3 = a && x; +var a4 = a && b; +var a5 = a && v; +var a6 = a && u; +var a7 = a && n; +var a8 = a && z; +var s1 = s && a; +var s2 = s && s; +var s3 = s && x; +var s4 = s && b; +var s5 = s && v; +var s6 = s && u; +var s7 = s && n; +var s8 = s && z; +var x1 = x && a; +var x2 = x && s; +var x3 = x && x; +var x4 = x && b; +var x5 = x && v; +var x6 = x && u; +var x7 = x && n; +var x8 = x && z; +var b1 = b && a; +var b2 = b && s; +var b3 = b && x; +var b4 = b && b; +var b5 = b && v; +var b6 = b && u; +var b7 = b && n; +var b8 = b && z; +var v1 = v && a; +var v2 = v && s; +var v3 = v && x; +var v4 = v && b; +var v5 = v && v; +var v6 = v && u; +var v7 = v && n; +var v8 = v && z; +var u1 = u && a; +var u2 = u && s; +var u3 = u && x; +var u4 = u && b; +var u5 = u && v; +var u6 = u && u; +var u7 = u && n; +var u8 = u && z; +var n1 = n && a; +var n2 = n && s; +var n3 = n && x; +var n4 = n && b; +var n5 = n && v; +var n6 = n && u; +var n7 = n && n; +var n8 = n && z; +var z1 = z && a; +var z2 = z && s; +var z3 = z && x; +var z4 = z && b; +var z5 = z && v; +var z6 = z && u; +var z7 = z && n; +var z8 = z && z; diff --git a/tests/baselines/reference/logicalAndOperatorStrictMode.symbols b/tests/baselines/reference/logicalAndOperatorStrictMode.symbols new file mode 100644 index 00000000000..e3425a26149 --- /dev/null +++ b/tests/baselines/reference/logicalAndOperatorStrictMode.symbols @@ -0,0 +1,351 @@ +=== tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts === + +const a = [0]; +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const s = ""; +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const x = 0; +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const b = false; +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const v: void = undefined; +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>undefined : Symbol(undefined) + +const u = undefined; +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>undefined : Symbol(undefined) + +const n = null; +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const z = s || x || u; +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const a1 = a && a; +>a1 : Symbol(a1, Decl(logicalAndOperatorStrictMode.ts, 10, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const a2 = a && s; +>a2 : Symbol(a2, Decl(logicalAndOperatorStrictMode.ts, 11, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const a3 = a && x; +>a3 : Symbol(a3, Decl(logicalAndOperatorStrictMode.ts, 12, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const a4 = a && b; +>a4 : Symbol(a4, Decl(logicalAndOperatorStrictMode.ts, 13, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const a5 = a && v; +>a5 : Symbol(a5, Decl(logicalAndOperatorStrictMode.ts, 14, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const a6 = a && u; +>a6 : Symbol(a6, Decl(logicalAndOperatorStrictMode.ts, 15, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const a7 = a && n; +>a7 : Symbol(a7, Decl(logicalAndOperatorStrictMode.ts, 16, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const a8 = a && z; +>a8 : Symbol(a8, Decl(logicalAndOperatorStrictMode.ts, 17, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const s1 = s && a; +>s1 : Symbol(s1, Decl(logicalAndOperatorStrictMode.ts, 19, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const s2 = s && s; +>s2 : Symbol(s2, Decl(logicalAndOperatorStrictMode.ts, 20, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const s3 = s && x; +>s3 : Symbol(s3, Decl(logicalAndOperatorStrictMode.ts, 21, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const s4 = s && b; +>s4 : Symbol(s4, Decl(logicalAndOperatorStrictMode.ts, 22, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const s5 = s && v; +>s5 : Symbol(s5, Decl(logicalAndOperatorStrictMode.ts, 23, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const s6 = s && u; +>s6 : Symbol(s6, Decl(logicalAndOperatorStrictMode.ts, 24, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const s7 = s && n; +>s7 : Symbol(s7, Decl(logicalAndOperatorStrictMode.ts, 25, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const s8 = s && z; +>s8 : Symbol(s8, Decl(logicalAndOperatorStrictMode.ts, 26, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const x1 = x && a; +>x1 : Symbol(x1, Decl(logicalAndOperatorStrictMode.ts, 28, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const x2 = x && s; +>x2 : Symbol(x2, Decl(logicalAndOperatorStrictMode.ts, 29, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const x3 = x && x; +>x3 : Symbol(x3, Decl(logicalAndOperatorStrictMode.ts, 30, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const x4 = x && b; +>x4 : Symbol(x4, Decl(logicalAndOperatorStrictMode.ts, 31, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const x5 = x && v; +>x5 : Symbol(x5, Decl(logicalAndOperatorStrictMode.ts, 32, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const x6 = x && u; +>x6 : Symbol(x6, Decl(logicalAndOperatorStrictMode.ts, 33, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const x7 = x && n; +>x7 : Symbol(x7, Decl(logicalAndOperatorStrictMode.ts, 34, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const x8 = x && z; +>x8 : Symbol(x8, Decl(logicalAndOperatorStrictMode.ts, 35, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const b1 = b && a; +>b1 : Symbol(b1, Decl(logicalAndOperatorStrictMode.ts, 37, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const b2 = b && s; +>b2 : Symbol(b2, Decl(logicalAndOperatorStrictMode.ts, 38, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const b3 = b && x; +>b3 : Symbol(b3, Decl(logicalAndOperatorStrictMode.ts, 39, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const b4 = b && b; +>b4 : Symbol(b4, Decl(logicalAndOperatorStrictMode.ts, 40, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const b5 = b && v; +>b5 : Symbol(b5, Decl(logicalAndOperatorStrictMode.ts, 41, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const b6 = b && u; +>b6 : Symbol(b6, Decl(logicalAndOperatorStrictMode.ts, 42, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const b7 = b && n; +>b7 : Symbol(b7, Decl(logicalAndOperatorStrictMode.ts, 43, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const b8 = b && z; +>b8 : Symbol(b8, Decl(logicalAndOperatorStrictMode.ts, 44, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const v1 = v && a; +>v1 : Symbol(v1, Decl(logicalAndOperatorStrictMode.ts, 46, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const v2 = v && s; +>v2 : Symbol(v2, Decl(logicalAndOperatorStrictMode.ts, 47, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const v3 = v && x; +>v3 : Symbol(v3, Decl(logicalAndOperatorStrictMode.ts, 48, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const v4 = v && b; +>v4 : Symbol(v4, Decl(logicalAndOperatorStrictMode.ts, 49, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const v5 = v && v; +>v5 : Symbol(v5, Decl(logicalAndOperatorStrictMode.ts, 50, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const v6 = v && u; +>v6 : Symbol(v6, Decl(logicalAndOperatorStrictMode.ts, 51, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const v7 = v && n; +>v7 : Symbol(v7, Decl(logicalAndOperatorStrictMode.ts, 52, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const v8 = v && z; +>v8 : Symbol(v8, Decl(logicalAndOperatorStrictMode.ts, 53, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const u1 = u && a; +>u1 : Symbol(u1, Decl(logicalAndOperatorStrictMode.ts, 55, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const u2 = u && s; +>u2 : Symbol(u2, Decl(logicalAndOperatorStrictMode.ts, 56, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const u3 = u && x; +>u3 : Symbol(u3, Decl(logicalAndOperatorStrictMode.ts, 57, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const u4 = u && b; +>u4 : Symbol(u4, Decl(logicalAndOperatorStrictMode.ts, 58, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const u5 = u && v; +>u5 : Symbol(u5, Decl(logicalAndOperatorStrictMode.ts, 59, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const u6 = u && u; +>u6 : Symbol(u6, Decl(logicalAndOperatorStrictMode.ts, 60, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const u7 = u && n; +>u7 : Symbol(u7, Decl(logicalAndOperatorStrictMode.ts, 61, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const u8 = u && z; +>u8 : Symbol(u8, Decl(logicalAndOperatorStrictMode.ts, 62, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const n1 = n && a; +>n1 : Symbol(n1, Decl(logicalAndOperatorStrictMode.ts, 64, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const n2 = n && s; +>n2 : Symbol(n2, Decl(logicalAndOperatorStrictMode.ts, 65, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const n3 = n && x; +>n3 : Symbol(n3, Decl(logicalAndOperatorStrictMode.ts, 66, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const n4 = n && b; +>n4 : Symbol(n4, Decl(logicalAndOperatorStrictMode.ts, 67, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const n5 = n && v; +>n5 : Symbol(n5, Decl(logicalAndOperatorStrictMode.ts, 68, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const n6 = n && u; +>n6 : Symbol(n6, Decl(logicalAndOperatorStrictMode.ts, 69, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const n7 = n && n; +>n7 : Symbol(n7, Decl(logicalAndOperatorStrictMode.ts, 70, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const n8 = n && z; +>n8 : Symbol(n8, Decl(logicalAndOperatorStrictMode.ts, 71, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + +const z1 = z && a; +>z1 : Symbol(z1, Decl(logicalAndOperatorStrictMode.ts, 73, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>a : Symbol(a, Decl(logicalAndOperatorStrictMode.ts, 1, 5)) + +const z2 = z && s; +>z2 : Symbol(z2, Decl(logicalAndOperatorStrictMode.ts, 74, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>s : Symbol(s, Decl(logicalAndOperatorStrictMode.ts, 2, 5)) + +const z3 = z && x; +>z3 : Symbol(z3, Decl(logicalAndOperatorStrictMode.ts, 75, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>x : Symbol(x, Decl(logicalAndOperatorStrictMode.ts, 3, 5)) + +const z4 = z && b; +>z4 : Symbol(z4, Decl(logicalAndOperatorStrictMode.ts, 76, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>b : Symbol(b, Decl(logicalAndOperatorStrictMode.ts, 4, 5)) + +const z5 = z && v; +>z5 : Symbol(z5, Decl(logicalAndOperatorStrictMode.ts, 77, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>v : Symbol(v, Decl(logicalAndOperatorStrictMode.ts, 5, 5)) + +const z6 = z && u; +>z6 : Symbol(z6, Decl(logicalAndOperatorStrictMode.ts, 78, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>u : Symbol(u, Decl(logicalAndOperatorStrictMode.ts, 6, 5)) + +const z7 = z && n; +>z7 : Symbol(z7, Decl(logicalAndOperatorStrictMode.ts, 79, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>n : Symbol(n, Decl(logicalAndOperatorStrictMode.ts, 7, 5)) + +const z8 = z && z; +>z8 : Symbol(z8, Decl(logicalAndOperatorStrictMode.ts, 80, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) +>z : Symbol(z, Decl(logicalAndOperatorStrictMode.ts, 8, 5)) + diff --git a/tests/baselines/reference/logicalAndOperatorStrictMode.types b/tests/baselines/reference/logicalAndOperatorStrictMode.types new file mode 100644 index 00000000000..ca225d4561a --- /dev/null +++ b/tests/baselines/reference/logicalAndOperatorStrictMode.types @@ -0,0 +1,423 @@ +=== tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts === + +const a = [0]; +>a : number[] +>[0] : number[] +>0 : number + +const s = ""; +>s : string +>"" : string + +const x = 0; +>x : number +>0 : number + +const b = false; +>b : boolean +>false : boolean + +const v: void = undefined; +>v : void +>undefined : undefined + +const u = undefined; +>u : undefined +>undefined : undefined + +const n = null; +>n : null +>null : null + +const z = s || x || u; +>z : string | number | undefined +>s || x || u : string | number | undefined +>s || x : string | number +>s : string +>x : number +>u : undefined + +const a1 = a && a; +>a1 : number[] +>a && a : number[] +>a : number[] +>a : number[] + +const a2 = a && s; +>a2 : string +>a && s : string +>a : number[] +>s : string + +const a3 = a && x; +>a3 : number +>a && x : number +>a : number[] +>x : number + +const a4 = a && b; +>a4 : boolean +>a && b : boolean +>a : number[] +>b : boolean + +const a5 = a && v; +>a5 : void +>a && v : void +>a : number[] +>v : void + +const a6 = a && u; +>a6 : undefined +>a && u : undefined +>a : number[] +>u : undefined + +const a7 = a && n; +>a7 : null +>a && n : null +>a : number[] +>n : null + +const a8 = a && z; +>a8 : string | number | undefined +>a && z : string | number | undefined +>a : number[] +>z : string | number | undefined + +const s1 = s && a; +>s1 : number[] | string +>s && a : number[] | string +>s : string +>a : number[] + +const s2 = s && s; +>s2 : string +>s && s : string +>s : string +>s : string + +const s3 = s && x; +>s3 : number | string +>s && x : number | string +>s : string +>x : number + +const s4 = s && b; +>s4 : boolean | string +>s && b : boolean | string +>s : string +>b : boolean + +const s5 = s && v; +>s5 : void | string +>s && v : void | string +>s : string +>v : void + +const s6 = s && u; +>s6 : string | undefined +>s && u : string | undefined +>s : string +>u : undefined + +const s7 = s && n; +>s7 : string | null +>s && n : string | null +>s : string +>n : null + +const s8 = s && z; +>s8 : string | number | undefined +>s && z : string | number | undefined +>s : string +>z : string | number | undefined + +const x1 = x && a; +>x1 : number[] | number +>x && a : number[] | number +>x : number +>a : number[] + +const x2 = x && s; +>x2 : string | number +>x && s : string | number +>x : number +>s : string + +const x3 = x && x; +>x3 : number +>x && x : number +>x : number +>x : number + +const x4 = x && b; +>x4 : boolean | number +>x && b : boolean | number +>x : number +>b : boolean + +const x5 = x && v; +>x5 : void | number +>x && v : void | number +>x : number +>v : void + +const x6 = x && u; +>x6 : number | undefined +>x && u : number | undefined +>x : number +>u : undefined + +const x7 = x && n; +>x7 : number | null +>x && n : number | null +>x : number +>n : null + +const x8 = x && z; +>x8 : string | number | undefined +>x && z : string | number | undefined +>x : number +>z : string | number | undefined + +const b1 = b && a; +>b1 : number[] | boolean +>b && a : number[] | boolean +>b : boolean +>a : number[] + +const b2 = b && s; +>b2 : string | boolean +>b && s : string | boolean +>b : boolean +>s : string + +const b3 = b && x; +>b3 : number | boolean +>b && x : number | boolean +>b : boolean +>x : number + +const b4 = b && b; +>b4 : boolean +>b && b : boolean +>b : boolean +>b : boolean + +const b5 = b && v; +>b5 : void | boolean +>b && v : void | boolean +>b : boolean +>v : void + +const b6 = b && u; +>b6 : boolean | undefined +>b && u : boolean | undefined +>b : boolean +>u : undefined + +const b7 = b && n; +>b7 : boolean | null +>b && n : boolean | null +>b : boolean +>n : null + +const b8 = b && z; +>b8 : string | number | boolean | undefined +>b && z : string | number | boolean | undefined +>b : boolean +>z : string | number | undefined + +const v1 = v && a; +>v1 : number[] | void +>v && a : number[] | void +>v : void +>a : number[] + +const v2 = v && s; +>v2 : string | void +>v && s : string | void +>v : void +>s : string + +const v3 = v && x; +>v3 : number | void +>v && x : number | void +>v : void +>x : number + +const v4 = v && b; +>v4 : boolean | void +>v && b : boolean | void +>v : void +>b : boolean + +const v5 = v && v; +>v5 : void +>v && v : void +>v : void +>v : never + +const v6 = v && u; +>v6 : void +>v && u : void +>v : void +>u : undefined + +const v7 = v && n; +>v7 : void | null +>v && n : void | null +>v : void +>n : null + +const v8 = v && z; +>v8 : string | number | void +>v && z : string | number | void +>v : void +>z : string | number | undefined + +const u1 = u && a; +>u1 : number[] | undefined +>u && a : number[] | undefined +>u : undefined +>a : number[] + +const u2 = u && s; +>u2 : string | undefined +>u && s : string | undefined +>u : undefined +>s : string + +const u3 = u && x; +>u3 : number | undefined +>u && x : number | undefined +>u : undefined +>x : number + +const u4 = u && b; +>u4 : boolean | undefined +>u && b : boolean | undefined +>u : undefined +>b : boolean + +const u5 = u && v; +>u5 : void +>u && v : void +>u : undefined +>v : void + +const u6 = u && u; +>u6 : undefined +>u && u : undefined +>u : undefined +>u : never + +const u7 = u && n; +>u7 : null | undefined +>u && n : null | undefined +>u : undefined +>n : null + +const u8 = u && z; +>u8 : string | number | undefined +>u && z : string | number | undefined +>u : undefined +>z : string | number | undefined + +const n1 = n && a; +>n1 : number[] | null +>n && a : number[] | null +>n : null +>a : number[] + +const n2 = n && s; +>n2 : string | null +>n && s : string | null +>n : null +>s : string + +const n3 = n && x; +>n3 : number | null +>n && x : number | null +>n : null +>x : number + +const n4 = n && b; +>n4 : boolean | null +>n && b : boolean | null +>n : null +>b : boolean + +const n5 = n && v; +>n5 : void | null +>n && v : void | null +>n : null +>v : void + +const n6 = n && u; +>n6 : null | undefined +>n && u : null | undefined +>n : null +>u : undefined + +const n7 = n && n; +>n7 : null +>n && n : null +>n : null +>n : never + +const n8 = n && z; +>n8 : string | number | null | undefined +>n && z : string | number | null | undefined +>n : null +>z : string | number | undefined + +const z1 = z && a; +>z1 : number[] | string | number | undefined +>z && a : number[] | string | number | undefined +>z : string | number | undefined +>a : number[] + +const z2 = z && s; +>z2 : string | number | undefined +>z && s : string | number | undefined +>z : string | number | undefined +>s : string + +const z3 = z && x; +>z3 : number | string | undefined +>z && x : number | string | undefined +>z : string | number | undefined +>x : number + +const z4 = z && b; +>z4 : boolean | string | number | undefined +>z && b : boolean | string | number | undefined +>z : string | number | undefined +>b : boolean + +const z5 = z && v; +>z5 : void | string | number +>z && v : void | string | number +>z : string | number | undefined +>v : void + +const z6 = z && u; +>z6 : string | number | undefined +>z && u : string | number | undefined +>z : string | number | undefined +>u : undefined + +const z7 = z && n; +>z7 : string | number | null | undefined +>z && n : string | number | null | undefined +>z : string | number | undefined +>n : null + +const z8 = z && z; +>z8 : string | number | undefined +>z && z : string | number | undefined +>z : string | number | undefined +>z : string | number + diff --git a/tests/baselines/reference/moduleResolutionWithExtensions.js b/tests/baselines/reference/moduleResolutionWithExtensions.js new file mode 100644 index 00000000000..df12a3531bc --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions.js @@ -0,0 +1,46 @@ +//// [tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts] //// + +//// [a.ts] + +export default 0; + +// No extension: '.ts' added +//// [b.ts] +import a from './a'; + +// Matching extension +//// [c.ts] +import a from './a.ts'; + +// '.js' extension: stripped and replaced with '.ts' +//// [d.ts] +import a from './a.js'; + +//// [jquery.d.ts] +declare var x: number; +export default x; + +// No extension: '.d.ts' added +//// [jquery_user_1.ts] +import j from "./jquery"; + +// '.js' extension: stripped and replaced with '.d.ts' +//// [jquery_user_1.ts] +import j from "./jquery.js" + + +//// [a.js] +"use strict"; +exports.__esModule = true; +exports["default"] = 0; +// No extension: '.ts' added +//// [b.js] +"use strict"; +// Matching extension +//// [c.js] +"use strict"; +// '.js' extension: stripped and replaced with '.ts' +//// [d.js] +"use strict"; +//// [jquery_user_1.js] +"use strict"; diff --git a/tests/baselines/reference/moduleResolutionWithExtensions.symbols b/tests/baselines/reference/moduleResolutionWithExtensions.symbols new file mode 100644 index 00000000000..e9934946a64 --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions.symbols @@ -0,0 +1,37 @@ +=== /src/a.ts === + +No type information for this code.export default 0; +No type information for this code. +No type information for this code.// No extension: '.ts' added +No type information for this code.=== /src/b.ts === +import a from './a'; +>a : Symbol(a, Decl(b.ts, 0, 6)) + +// Matching extension +=== /src/c.ts === +import a from './a.ts'; +>a : Symbol(a, Decl(c.ts, 0, 6)) + +// '.js' extension: stripped and replaced with '.ts' +=== /src/d.ts === +import a from './a.js'; +>a : Symbol(a, Decl(d.ts, 0, 6)) + +=== /src/jquery.d.ts === +declare var x: number; +>x : Symbol(x, Decl(jquery.d.ts, 0, 11)) + +export default x; +>x : Symbol(x, Decl(jquery.d.ts, 0, 11)) + +// No extension: '.d.ts' added +=== /src/jquery_user_1.ts === +import j from "./jquery"; +>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) + +// '.js' extension: stripped and replaced with '.d.ts' +=== /src/jquery_user_1.ts === +import j from "./jquery.js" +>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) +>j : Symbol(j, Decl(jquery_user_1.ts, 0, 6)) + diff --git a/tests/baselines/reference/moduleResolutionWithExtensions.trace.json b/tests/baselines/reference/moduleResolutionWithExtensions.trace.json new file mode 100644 index 00000000000..6e6589d7c1f --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions.trace.json @@ -0,0 +1,32 @@ +[ + "======== Resolving module './a' from '/src/b.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location '/src/a'.", + "File '/src/a.ts' exist - use it as a name resolution result.", + "======== Module name './a' was successfully resolved to '/src/a.ts'. ========", + "======== Resolving module './a.ts' from '/src/c.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location '/src/a.ts'.", + "File '/src/a.ts' exist - use it as a name resolution result.", + "======== Module name './a.ts' was successfully resolved to '/src/a.ts'. ========", + "======== Resolving module './a.js' from '/src/d.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location '/src/a.js'.", + "File '/src/a.js.ts' does not exist.", + "File '/src/a.js.tsx' does not exist.", + "File '/src/a.js.d.ts' does not exist.", + "File name '/src/a.js' has a '.js' extension - stripping it", + "File '/src/a.ts' exist - use it as a name resolution result.", + "======== Module name './a.js' was successfully resolved to '/src/a.ts'. ========", + "======== Resolving module './jquery.js' from '/src/jquery_user_1.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module as file / folder, candidate module location '/src/jquery.js'.", + "File '/src/jquery.js.ts' does not exist.", + "File '/src/jquery.js.tsx' does not exist.", + "File '/src/jquery.js.d.ts' does not exist.", + "File name '/src/jquery.js' has a '.js' extension - stripping it", + "File '/src/jquery.ts' does not exist.", + "File '/src/jquery.tsx' does not exist.", + "File '/src/jquery.d.ts' exist - use it as a name resolution result.", + "======== Module name './jquery.js' was successfully resolved to '/src/jquery.d.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/moduleResolutionWithExtensions.types b/tests/baselines/reference/moduleResolutionWithExtensions.types new file mode 100644 index 00000000000..fbc0091ee3b --- /dev/null +++ b/tests/baselines/reference/moduleResolutionWithExtensions.types @@ -0,0 +1,37 @@ +=== /src/a.ts === + +No type information for this code.export default 0; +No type information for this code. +No type information for this code.// No extension: '.ts' added +No type information for this code.=== /src/b.ts === +import a from './a'; +>a : number + +// Matching extension +=== /src/c.ts === +import a from './a.ts'; +>a : number + +// '.js' extension: stripped and replaced with '.ts' +=== /src/d.ts === +import a from './a.js'; +>a : number + +=== /src/jquery.d.ts === +declare var x: number; +>x : number + +export default x; +>x : number + +// No extension: '.d.ts' added +=== /src/jquery_user_1.ts === +import j from "./jquery"; +>j : number + +// '.js' extension: stripped and replaced with '.d.ts' +=== /src/jquery_user_1.ts === +import j from "./jquery.js" +>j : number +>j : number + diff --git a/tests/baselines/reference/nameWithFileExtension.errors.txt b/tests/baselines/reference/nameWithFileExtension.errors.txt deleted file mode 100644 index 2aa341271cd..00000000000 --- a/tests/baselines/reference/nameWithFileExtension.errors.txt +++ /dev/null @@ -1,12 +0,0 @@ -tests/cases/conformance/externalModules/foo_1.ts(1,22): error TS2307: Cannot find module './foo_0.js'. - - -==== tests/cases/conformance/externalModules/foo_1.ts (1 errors) ==== - import foo = require('./foo_0.js'); - ~~~~~~~~~~~~ -!!! error TS2307: Cannot find module './foo_0.js'. - var x = foo.foo + 42; - -==== tests/cases/conformance/externalModules/foo_0.ts (0 errors) ==== - export var foo = 42; - \ No newline at end of file diff --git a/tests/baselines/reference/nameWithFileExtension.js b/tests/baselines/reference/nameWithFileExtension.js index 2d487ede245..8a7506c2d42 100644 --- a/tests/baselines/reference/nameWithFileExtension.js +++ b/tests/baselines/reference/nameWithFileExtension.js @@ -8,6 +8,9 @@ import foo = require('./foo_0.js'); var x = foo.foo + 42; +//// [foo_0.js] +"use strict"; +exports.foo = 42; //// [foo_1.js] "use strict"; var foo = require('./foo_0.js'); diff --git a/tests/baselines/reference/nameWithFileExtension.symbols b/tests/baselines/reference/nameWithFileExtension.symbols new file mode 100644 index 00000000000..61e6a768ea6 --- /dev/null +++ b/tests/baselines/reference/nameWithFileExtension.symbols @@ -0,0 +1,14 @@ +=== tests/cases/conformance/externalModules/foo_1.ts === +import foo = require('./foo_0.js'); +>foo : Symbol(foo, Decl(foo_1.ts, 0, 0)) + +var x = foo.foo + 42; +>x : Symbol(x, Decl(foo_1.ts, 1, 3)) +>foo.foo : Symbol(foo.foo, Decl(foo_0.ts, 0, 10)) +>foo : Symbol(foo, Decl(foo_1.ts, 0, 0)) +>foo : Symbol(foo.foo, Decl(foo_0.ts, 0, 10)) + +=== tests/cases/conformance/externalModules/foo_0.ts === +export var foo = 42; +>foo : Symbol(foo, Decl(foo_0.ts, 0, 10)) + diff --git a/tests/baselines/reference/nameWithFileExtension.types b/tests/baselines/reference/nameWithFileExtension.types new file mode 100644 index 00000000000..a0fca3d4562 --- /dev/null +++ b/tests/baselines/reference/nameWithFileExtension.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/externalModules/foo_1.ts === +import foo = require('./foo_0.js'); +>foo : typeof foo + +var x = foo.foo + 42; +>x : number +>foo.foo + 42 : number +>foo.foo : number +>foo : typeof foo +>foo : number +>42 : number + +=== tests/cases/conformance/externalModules/foo_0.ts === +export var foo = 42; +>foo : number +>42 : number + diff --git a/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.errors.txt b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.errors.txt deleted file mode 100644 index bfa77ff5a3e..00000000000 --- a/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.errors.txt +++ /dev/null @@ -1,7 +0,0 @@ -error TS5053: Option 'sourceRoot' cannot be specified with option 'inlineSources'. - - -!!! error TS5053: Option 'sourceRoot' cannot be specified with option 'inlineSources'. -==== tests/cases/compiler/optionsSourcemapInlineSourcesSourceRoot.ts (0 errors) ==== - - var a = 10; \ No newline at end of file diff --git a/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.symbols b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.symbols new file mode 100644 index 00000000000..76f2dc5fdc4 --- /dev/null +++ b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.symbols @@ -0,0 +1,5 @@ +=== tests/cases/compiler/optionsSourcemapInlineSourcesSourceRoot.ts === + +var a = 10; +>a : Symbol(a, Decl(optionsSourcemapInlineSourcesSourceRoot.ts, 1, 3)) + diff --git a/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.types b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.types new file mode 100644 index 00000000000..3689afc382c --- /dev/null +++ b/tests/baselines/reference/optionsSourcemapInlineSourcesSourceRoot.types @@ -0,0 +1,6 @@ +=== tests/cases/compiler/optionsSourcemapInlineSourcesSourceRoot.ts === + +var a = 10; +>a : number +>10 : number + diff --git a/tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt b/tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt index 7c071fdc208..e9f8842d5cb 100644 --- a/tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt +++ b/tests/baselines/reference/project/cantFindTheModule/amd/cantFindTheModule.errors.txt @@ -1,12 +1,12 @@ -decl.ts(1,26): error TS2307: Cannot find module './foo/bar.js'. +decl.ts(1,26): error TS2307: Cannot find module './foo/bar.tx'. decl.ts(2,26): error TS2307: Cannot find module 'baz'. decl.ts(3,26): error TS2307: Cannot find module './baz'. ==== decl.ts (3 errors) ==== - import modErr = require("./foo/bar.js"); + import modErr = require("./foo/bar.tx"); ~~~~~~~~~~~~~~ -!!! error TS2307: Cannot find module './foo/bar.js'. +!!! error TS2307: Cannot find module './foo/bar.tx'. import modErr1 = require("baz"); ~~~~~ !!! error TS2307: Cannot find module 'baz'. diff --git a/tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt b/tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt index 7c071fdc208..e9f8842d5cb 100644 --- a/tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt +++ b/tests/baselines/reference/project/cantFindTheModule/node/cantFindTheModule.errors.txt @@ -1,12 +1,12 @@ -decl.ts(1,26): error TS2307: Cannot find module './foo/bar.js'. +decl.ts(1,26): error TS2307: Cannot find module './foo/bar.tx'. decl.ts(2,26): error TS2307: Cannot find module 'baz'. decl.ts(3,26): error TS2307: Cannot find module './baz'. ==== decl.ts (3 errors) ==== - import modErr = require("./foo/bar.js"); + import modErr = require("./foo/bar.tx"); ~~~~~~~~~~~~~~ -!!! error TS2307: Cannot find module './foo/bar.js'. +!!! error TS2307: Cannot find module './foo/bar.tx'. import modErr1 = require("baz"); ~~~~~ !!! error TS2307: Cannot find module 'baz'. diff --git a/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/amd/mapRootSourceRootWithNoSourceMapOption.errors.txt b/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/amd/mapRootSourceRootWithNoSourceMapOption.errors.txt index 44d2c93d9a5..2a780bdeda0 100644 --- a/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/amd/mapRootSourceRootWithNoSourceMapOption.errors.txt +++ b/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/amd/mapRootSourceRootWithNoSourceMapOption.errors.txt @@ -1,9 +1,9 @@ +error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. error TS5052: Option 'mapRoot' cannot be specified without specifying option 'sourceMap'. -error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +!!! error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. !!! error TS5052: Option 'mapRoot' cannot be specified without specifying option 'sourceMap'. -!!! error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. ==== m1.ts (0 errors) ==== var m1_a1 = 10; class m1_c1 { diff --git a/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/node/mapRootSourceRootWithNoSourceMapOption.errors.txt b/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/node/mapRootSourceRootWithNoSourceMapOption.errors.txt index 44d2c93d9a5..2a780bdeda0 100644 --- a/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/node/mapRootSourceRootWithNoSourceMapOption.errors.txt +++ b/tests/baselines/reference/project/mapRootSourceRootWithNoSourceMapOption/node/mapRootSourceRootWithNoSourceMapOption.errors.txt @@ -1,9 +1,9 @@ +error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. error TS5052: Option 'mapRoot' cannot be specified without specifying option 'sourceMap'. -error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +!!! error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. !!! error TS5052: Option 'mapRoot' cannot be specified without specifying option 'sourceMap'. -!!! error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. ==== m1.ts (0 errors) ==== var m1_a1 = 10; class m1_c1 { diff --git a/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/amd/sourceRootWithNoSourceMapOption.errors.txt b/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/amd/sourceRootWithNoSourceMapOption.errors.txt index 56dab79dacf..b79cae049ff 100644 --- a/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/amd/sourceRootWithNoSourceMapOption.errors.txt +++ b/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/amd/sourceRootWithNoSourceMapOption.errors.txt @@ -1,7 +1,7 @@ -error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. -!!! error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +!!! error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. ==== m1.ts (0 errors) ==== var m1_a1 = 10; class m1_c1 { diff --git a/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/node/sourceRootWithNoSourceMapOption.errors.txt b/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/node/sourceRootWithNoSourceMapOption.errors.txt index 56dab79dacf..b79cae049ff 100644 --- a/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/node/sourceRootWithNoSourceMapOption.errors.txt +++ b/tests/baselines/reference/project/sourceRootWithNoSourceMapOption/node/sourceRootWithNoSourceMapOption.errors.txt @@ -1,7 +1,7 @@ -error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. -!!! error TS5052: Option 'sourceRoot' cannot be specified without specifying option 'sourceMap'. +!!! error TS5051: Option 'sourceRoot can only be used when either option '--inlineSourceMap' or option '--sourceMap' is provided. ==== m1.ts (0 errors) ==== var m1_a1 = 10; class m1_c1 { diff --git a/tests/baselines/reference/stringLiteralTypesAsTags01.types b/tests/baselines/reference/stringLiteralTypesAsTags01.types index 7b8ac0a8c02..64b099b34f2 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags01.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags01.types @@ -116,6 +116,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : never ->x : never +>d : A & B +>x : A & B } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags02.types b/tests/baselines/reference/stringLiteralTypesAsTags02.types index 290402cd299..92b294a2498 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags02.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags02.types @@ -110,6 +110,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : never ->x : never +>d : A & B +>x : A & B } diff --git a/tests/baselines/reference/stringLiteralTypesAsTags03.types b/tests/baselines/reference/stringLiteralTypesAsTags03.types index 9d004982dda..49ae3da4be0 100644 --- a/tests/baselines/reference/stringLiteralTypesAsTags03.types +++ b/tests/baselines/reference/stringLiteralTypesAsTags03.types @@ -113,6 +113,6 @@ if (!hasKind(x, "B")) { } else { let d = x; ->d : never ->x : never +>d : A & B +>x : A & B } diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.js b/tests/baselines/reference/typeGuardIntersectionTypes.js new file mode 100644 index 00000000000..6de7d06e323 --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.js @@ -0,0 +1,179 @@ +//// [typeGuardIntersectionTypes.ts] + +interface X { + x: string; +} + +interface Y { + y: string; +} + +interface Z { + z: string; +} + +declare function isX(obj: any): obj is X; +declare function isY(obj: any): obj is Y; +declare function isZ(obj: any): obj is Z; + +function f1(obj: Object) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} + +// Repro from #8911 + +// two interfaces +interface A { + a: string; +} + +interface B { + b: string; +} + +// a type guard for B +function isB(toTest: any): toTest is B { + return toTest && toTest.b; +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { + if (isB(a)) { + return a; + } else { + return null; + } +} + +// Repro from #9016 + +declare function log(s: string): void; + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +interface Legged { legs: number; } +interface Winged { wings: boolean; } + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { + + // All beasts with legs + if (hasLegs(beast)) { + + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { + log(`pegasus - 4 legs, wings`); + } + else if (beast.legs === 2) { + log(`bird - 2 legs, wings`); + } + else { + log(`unknown - ${beast.legs} legs, wings`); + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { + log(`quetzalcoatl - no legs, wings`) + } + else { + log(`snake - no legs, no wings`) + } + } +} + +function beastFoo(beast: Object) { + if (hasWings(beast) && hasLegs(beast)) { + beast; // Winged & Legged + } + else { + beast; + } + + if (hasLegs(beast) && hasWings(beast)) { + beast; // Legged & Winged + } +} + +//// [typeGuardIntersectionTypes.js] +function f1(obj) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} +// a type guard for B +function isB(toTest) { + return toTest && toTest.b; +} +// a function that turns an A into an A & B +function union(a) { + if (isB(a)) { + return a; + } + else { + return null; + } +} +// Beast feature detection via user-defined type guards +function hasLegs(x) { return x && typeof x.legs === 'number'; } +function hasWings(x) { return x && !!x.wings; } +// Function to identify a given beast by detecting its features +function identifyBeast(beast) { + // All beasts with legs + if (hasLegs(beast)) { + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { + log("pegasus - 4 legs, wings"); + } + else if (beast.legs === 2) { + log("bird - 2 legs, wings"); + } + else { + log("unknown - " + beast.legs + " legs, wings"); + } + } + else { + log("manbearpig - " + beast.legs + " legs, no wings"); + } + } + else { + if (hasWings(beast)) { + log("quetzalcoatl - no legs, wings"); + } + else { + log("snake - no legs, no wings"); + } + } +} +function beastFoo(beast) { + if (hasWings(beast) && hasLegs(beast)) { + beast; // Winged & Legged + } + else { + beast; + } + if (hasLegs(beast) && hasWings(beast)) { + beast; // Legged & Winged + } +} diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.symbols b/tests/baselines/reference/typeGuardIntersectionTypes.symbols new file mode 100644 index 00000000000..ea8e903c765 --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.symbols @@ -0,0 +1,258 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts === + +interface X { +>X : Symbol(X, Decl(typeGuardIntersectionTypes.ts, 0, 0)) + + x: string; +>x : Symbol(X.x, Decl(typeGuardIntersectionTypes.ts, 1, 13)) +} + +interface Y { +>Y : Symbol(Y, Decl(typeGuardIntersectionTypes.ts, 3, 1)) + + y: string; +>y : Symbol(Y.y, Decl(typeGuardIntersectionTypes.ts, 5, 13)) +} + +interface Z { +>Z : Symbol(Z, Decl(typeGuardIntersectionTypes.ts, 7, 1)) + + z: string; +>z : Symbol(Z.z, Decl(typeGuardIntersectionTypes.ts, 9, 13)) +} + +declare function isX(obj: any): obj is X; +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 13, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 13, 21)) +>X : Symbol(X, Decl(typeGuardIntersectionTypes.ts, 0, 0)) + +declare function isY(obj: any): obj is Y; +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 14, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 14, 21)) +>Y : Symbol(Y, Decl(typeGuardIntersectionTypes.ts, 3, 1)) + +declare function isZ(obj: any): obj is Z; +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 15, 21)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 15, 21)) +>Z : Symbol(Z, Decl(typeGuardIntersectionTypes.ts, 7, 1)) + +function f1(obj: Object) { +>f1 : Symbol(f1, Decl(typeGuardIntersectionTypes.ts, 15, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + if (isX(obj) || isY(obj) || isZ(obj)) { +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + + obj; +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + } + if (isX(obj) && isY(obj) && isZ(obj)) { +>isX : Symbol(isX, Decl(typeGuardIntersectionTypes.ts, 11, 1)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isY : Symbol(isY, Decl(typeGuardIntersectionTypes.ts, 13, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) +>isZ : Symbol(isZ, Decl(typeGuardIntersectionTypes.ts, 14, 41)) +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + + obj; +>obj : Symbol(obj, Decl(typeGuardIntersectionTypes.ts, 17, 12)) + } +} + +// Repro from #8911 + +// two interfaces +interface A { +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) + + a: string; +>a : Symbol(A.a, Decl(typeGuardIntersectionTypes.ts, 29, 13)) +} + +interface B { +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + b: string; +>b : Symbol(B.b, Decl(typeGuardIntersectionTypes.ts, 33, 13)) +} + +// a type guard for B +function isB(toTest: any): toTest is B { +>isB : Symbol(isB, Decl(typeGuardIntersectionTypes.ts, 35, 1)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + return toTest && toTest.b; +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +>toTest : Symbol(toTest, Decl(typeGuardIntersectionTypes.ts, 38, 13)) +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { +>union : Symbol(union, Decl(typeGuardIntersectionTypes.ts, 40, 1)) +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) +>A : Symbol(A, Decl(typeGuardIntersectionTypes.ts, 24, 1)) +>B : Symbol(B, Decl(typeGuardIntersectionTypes.ts, 31, 1)) + + if (isB(a)) { +>isB : Symbol(isB, Decl(typeGuardIntersectionTypes.ts, 35, 1)) +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) + + return a; +>a : Symbol(a, Decl(typeGuardIntersectionTypes.ts, 43, 15)) + + } else { + return null; + } +} + +// Repro from #9016 + +declare function log(s: string): void; +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>s : Symbol(s, Decl(typeGuardIntersectionTypes.ts, 53, 21)) + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) + +interface Legged { legs: number; } +>Legged : Symbol(Legged, Decl(typeGuardIntersectionTypes.ts, 56, 54)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + +interface Winged { wings: boolean; } +>Winged : Symbol(Winged, Decl(typeGuardIntersectionTypes.ts, 57, 37)) +>wings : Symbol(Winged.wings, Decl(typeGuardIntersectionTypes.ts, 58, 21)) + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>Legged : Symbol(Legged, Decl(typeGuardIntersectionTypes.ts, 56, 54)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>x.legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 61, 17)) +>legs : Symbol(Beast.legs, Decl(typeGuardIntersectionTypes.ts, 56, 38)) + +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>Winged : Symbol(Winged, Decl(typeGuardIntersectionTypes.ts, 57, 37)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>x.wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) +>x : Symbol(x, Decl(typeGuardIntersectionTypes.ts, 62, 18)) +>wings : Symbol(Beast.wings, Decl(typeGuardIntersectionTypes.ts, 56, 21)) + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { +>identifyBeast : Symbol(identifyBeast, Decl(typeGuardIntersectionTypes.ts, 62, 67)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>Beast : Symbol(Beast, Decl(typeGuardIntersectionTypes.ts, 53, 38)) + + // All beasts with legs + if (hasLegs(beast)) { +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + // All winged beasts with legs + if (hasWings(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + if (beast.legs === 4) { +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + + log(`pegasus - 4 legs, wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else if (beast.legs === 2) { +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + + log(`bird - 2 legs, wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else { + log(`unknown - ${beast.legs} legs, wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) +>beast.legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) +>legs : Symbol(Legged.legs, Decl(typeGuardIntersectionTypes.ts, 57, 21)) + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 65, 23)) + + log(`quetzalcoatl - no legs, wings`) +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + else { + log(`snake - no legs, no wings`) +>log : Symbol(log, Decl(typeGuardIntersectionTypes.ts, 49, 1)) + } + } +} + +function beastFoo(beast: Object) { +>beastFoo : Symbol(beastFoo, Decl(typeGuardIntersectionTypes.ts, 98, 1)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + if (hasWings(beast) && hasLegs(beast)) { +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + beast; // Winged & Legged +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + } + else { + beast; +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + } + + if (hasLegs(beast) && hasWings(beast)) { +>hasLegs : Symbol(hasLegs, Decl(typeGuardIntersectionTypes.ts, 58, 39)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) +>hasWings : Symbol(hasWings, Decl(typeGuardIntersectionTypes.ts, 61, 83)) +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + + beast; // Legged & Winged +>beast : Symbol(beast, Decl(typeGuardIntersectionTypes.ts, 100, 18)) + } +} diff --git a/tests/baselines/reference/typeGuardIntersectionTypes.types b/tests/baselines/reference/typeGuardIntersectionTypes.types new file mode 100644 index 00000000000..8be50453572 --- /dev/null +++ b/tests/baselines/reference/typeGuardIntersectionTypes.types @@ -0,0 +1,306 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts === + +interface X { +>X : X + + x: string; +>x : string +} + +interface Y { +>Y : Y + + y: string; +>y : string +} + +interface Z { +>Z : Z + + z: string; +>z : string +} + +declare function isX(obj: any): obj is X; +>isX : (obj: any) => obj is X +>obj : any +>obj : any +>X : X + +declare function isY(obj: any): obj is Y; +>isY : (obj: any) => obj is Y +>obj : any +>obj : any +>Y : Y + +declare function isZ(obj: any): obj is Z; +>isZ : (obj: any) => obj is Z +>obj : any +>obj : any +>Z : Z + +function f1(obj: Object) { +>f1 : (obj: Object) => void +>obj : Object +>Object : Object + + if (isX(obj) || isY(obj) || isZ(obj)) { +>isX(obj) || isY(obj) || isZ(obj) : boolean +>isX(obj) || isY(obj) : boolean +>isX(obj) : boolean +>isX : (obj: any) => obj is X +>obj : Object +>isY(obj) : boolean +>isY : (obj: any) => obj is Y +>obj : Object +>isZ(obj) : boolean +>isZ : (obj: any) => obj is Z +>obj : Object + + obj; +>obj : X | Y | Z + } + if (isX(obj) && isY(obj) && isZ(obj)) { +>isX(obj) && isY(obj) && isZ(obj) : boolean +>isX(obj) && isY(obj) : boolean +>isX(obj) : boolean +>isX : (obj: any) => obj is X +>obj : Object +>isY(obj) : boolean +>isY : (obj: any) => obj is Y +>obj : X +>isZ(obj) : boolean +>isZ : (obj: any) => obj is Z +>obj : X & Y + + obj; +>obj : X & Y & Z + } +} + +// Repro from #8911 + +// two interfaces +interface A { +>A : A + + a: string; +>a : string +} + +interface B { +>B : B + + b: string; +>b : string +} + +// a type guard for B +function isB(toTest: any): toTest is B { +>isB : (toTest: any) => toTest is B +>toTest : any +>toTest : any +>B : B + + return toTest && toTest.b; +>toTest && toTest.b : any +>toTest : any +>toTest.b : any +>toTest : any +>b : any +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { +>union : (a: A) => (A & B) | null +>a : A +>A : A +>A : A +>B : B +>null : null + + if (isB(a)) { +>isB(a) : boolean +>isB : (toTest: any) => toTest is B +>a : A + + return a; +>a : A & B + + } else { + return null; +>null : null + } +} + +// Repro from #9016 + +declare function log(s: string): void; +>log : (s: string) => void +>s : string + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +>Beast : Beast +>wings : boolean | undefined +>legs : number | undefined + +interface Legged { legs: number; } +>Legged : Legged +>legs : number + +interface Winged { wings: boolean; } +>Winged : Winged +>wings : boolean + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +>hasLegs : (x: Beast) => x is Legged +>x : Beast +>Beast : Beast +>x : any +>Legged : Legged +>x && typeof x.legs === 'number' : boolean +>x : Beast +>typeof x.legs === 'number' : boolean +>typeof x.legs : string +>x.legs : number | undefined +>x : Beast +>legs : number | undefined +>'number' : string + +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } +>hasWings : (x: Beast) => x is Winged +>x : Beast +>Beast : Beast +>x : any +>Winged : Winged +>x && !!x.wings : boolean +>x : Beast +>!!x.wings : boolean +>!x.wings : boolean +>x.wings : boolean | undefined +>x : Beast +>wings : boolean | undefined + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { +>identifyBeast : (beast: Beast) => void +>beast : Beast +>Beast : Beast + + // All beasts with legs + if (hasLegs(beast)) { +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Beast + + // All winged beasts with legs + if (hasWings(beast)) { +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Legged + + if (beast.legs === 4) { +>beast.legs === 4 : boolean +>beast.legs : number +>beast : Legged & Winged +>legs : number +>4 : number + + log(`pegasus - 4 legs, wings`); +>log(`pegasus - 4 legs, wings`) : void +>log : (s: string) => void +>`pegasus - 4 legs, wings` : string + } + else if (beast.legs === 2) { +>beast.legs === 2 : boolean +>beast.legs : number +>beast : Legged & Winged +>legs : number +>2 : number + + log(`bird - 2 legs, wings`); +>log(`bird - 2 legs, wings`) : void +>log : (s: string) => void +>`bird - 2 legs, wings` : string + } + else { + log(`unknown - ${beast.legs} legs, wings`); +>log(`unknown - ${beast.legs} legs, wings`) : void +>log : (s: string) => void +>`unknown - ${beast.legs} legs, wings` : string +>beast.legs : number +>beast : Legged & Winged +>legs : number + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); +>log(`manbearpig - ${beast.legs} legs, no wings`) : void +>log : (s: string) => void +>`manbearpig - ${beast.legs} legs, no wings` : string +>beast.legs : number +>beast : Legged +>legs : number + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Beast + + log(`quetzalcoatl - no legs, wings`) +>log(`quetzalcoatl - no legs, wings`) : void +>log : (s: string) => void +>`quetzalcoatl - no legs, wings` : string + } + else { + log(`snake - no legs, no wings`) +>log(`snake - no legs, no wings`) : void +>log : (s: string) => void +>`snake - no legs, no wings` : string + } + } +} + +function beastFoo(beast: Object) { +>beastFoo : (beast: Object) => void +>beast : Object +>Object : Object + + if (hasWings(beast) && hasLegs(beast)) { +>hasWings(beast) && hasLegs(beast) : boolean +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Object +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Winged + + beast; // Winged & Legged +>beast : Winged & Legged + } + else { + beast; +>beast : Object + } + + if (hasLegs(beast) && hasWings(beast)) { +>hasLegs(beast) && hasWings(beast) : boolean +>hasLegs(beast) : boolean +>hasLegs : (x: Beast) => x is Legged +>beast : Object +>hasWings(beast) : boolean +>hasWings : (x: Beast) => x is Winged +>beast : Legged + + beast; // Legged & Winged +>beast : Legged & Winged + } +} diff --git a/tests/baselines/reference/typeGuardsNestedAssignments.js b/tests/baselines/reference/typeGuardsNestedAssignments.js new file mode 100644 index 00000000000..f37db041006 --- /dev/null +++ b/tests/baselines/reference/typeGuardsNestedAssignments.js @@ -0,0 +1,86 @@ +//// [typeGuardsNestedAssignments.ts] + +class Foo { + x: string; +} + +declare function getFooOrNull(): Foo | null; +declare function getStringOrNumberOrNull(): string | number | null; + +function f1() { + let foo: Foo | null; + if ((foo = getFooOrNull()) !== null) { + foo; // Foo + } +} + +function f2() { + let foo1: Foo | null; + let foo2: Foo | null; + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { + foo1; // Foo | null + foo2; // Foo + } +} + +function f3() { + let obj: Object | null; + if ((obj = getFooOrNull()) instanceof Foo) { + obj; + } +} + +function f4() { + let x: string | number | null; + if (typeof (x = getStringOrNumberOrNull()) === "number") { + x; + } +} + +// Repro from #8851 + +const re = /./g +let match: RegExpExecArray | null + +while ((match = re.exec("xxx")) != null) { + const length = match[1].length + match[2].length +} + +//// [typeGuardsNestedAssignments.js] +var Foo = (function () { + function Foo() { + } + return Foo; +}()); +function f1() { + var foo; + if ((foo = getFooOrNull()) !== null) { + foo; // Foo + } +} +function f2() { + var foo1; + var foo2; + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { + foo1; // Foo | null + foo2; // Foo + } +} +function f3() { + var obj; + if ((obj = getFooOrNull()) instanceof Foo) { + obj; + } +} +function f4() { + var x; + if (typeof (x = getStringOrNumberOrNull()) === "number") { + x; + } +} +// Repro from #8851 +var re = /./g; +var match; +while ((match = re.exec("xxx")) != null) { + var length = match[1].length + match[2].length; +} diff --git a/tests/baselines/reference/typeGuardsNestedAssignments.symbols b/tests/baselines/reference/typeGuardsNestedAssignments.symbols new file mode 100644 index 00000000000..1cca9c7de73 --- /dev/null +++ b/tests/baselines/reference/typeGuardsNestedAssignments.symbols @@ -0,0 +1,113 @@ +=== tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts === + +class Foo { +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + x: string; +>x : Symbol(Foo.x, Decl(typeGuardsNestedAssignments.ts, 1, 11)) +} + +declare function getFooOrNull(): Foo | null; +>getFooOrNull : Symbol(getFooOrNull, Decl(typeGuardsNestedAssignments.ts, 3, 1)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + +declare function getStringOrNumberOrNull(): string | number | null; +>getStringOrNumberOrNull : Symbol(getStringOrNumberOrNull, Decl(typeGuardsNestedAssignments.ts, 5, 44)) + +function f1() { +>f1 : Symbol(f1, Decl(typeGuardsNestedAssignments.ts, 6, 67)) + + let foo: Foo | null; +>foo : Symbol(foo, Decl(typeGuardsNestedAssignments.ts, 9, 7)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + if ((foo = getFooOrNull()) !== null) { +>foo : Symbol(foo, Decl(typeGuardsNestedAssignments.ts, 9, 7)) +>getFooOrNull : Symbol(getFooOrNull, Decl(typeGuardsNestedAssignments.ts, 3, 1)) + + foo; // Foo +>foo : Symbol(foo, Decl(typeGuardsNestedAssignments.ts, 9, 7)) + } +} + +function f2() { +>f2 : Symbol(f2, Decl(typeGuardsNestedAssignments.ts, 13, 1)) + + let foo1: Foo | null; +>foo1 : Symbol(foo1, Decl(typeGuardsNestedAssignments.ts, 16, 7)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + let foo2: Foo | null; +>foo2 : Symbol(foo2, Decl(typeGuardsNestedAssignments.ts, 17, 7)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { +>foo1 : Symbol(foo1, Decl(typeGuardsNestedAssignments.ts, 16, 7)) +>getFooOrNull : Symbol(getFooOrNull, Decl(typeGuardsNestedAssignments.ts, 3, 1)) +>foo2 : Symbol(foo2, Decl(typeGuardsNestedAssignments.ts, 17, 7)) +>foo1 : Symbol(foo1, Decl(typeGuardsNestedAssignments.ts, 16, 7)) + + foo1; // Foo | null +>foo1 : Symbol(foo1, Decl(typeGuardsNestedAssignments.ts, 16, 7)) + + foo2; // Foo +>foo2 : Symbol(foo2, Decl(typeGuardsNestedAssignments.ts, 17, 7)) + } +} + +function f3() { +>f3 : Symbol(f3, Decl(typeGuardsNestedAssignments.ts, 22, 1)) + + let obj: Object | null; +>obj : Symbol(obj, Decl(typeGuardsNestedAssignments.ts, 25, 7)) +>Object : Symbol(Object, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + if ((obj = getFooOrNull()) instanceof Foo) { +>obj : Symbol(obj, Decl(typeGuardsNestedAssignments.ts, 25, 7)) +>getFooOrNull : Symbol(getFooOrNull, Decl(typeGuardsNestedAssignments.ts, 3, 1)) +>Foo : Symbol(Foo, Decl(typeGuardsNestedAssignments.ts, 0, 0)) + + obj; +>obj : Symbol(obj, Decl(typeGuardsNestedAssignments.ts, 25, 7)) + } +} + +function f4() { +>f4 : Symbol(f4, Decl(typeGuardsNestedAssignments.ts, 29, 1)) + + let x: string | number | null; +>x : Symbol(x, Decl(typeGuardsNestedAssignments.ts, 32, 7)) + + if (typeof (x = getStringOrNumberOrNull()) === "number") { +>x : Symbol(x, Decl(typeGuardsNestedAssignments.ts, 32, 7)) +>getStringOrNumberOrNull : Symbol(getStringOrNumberOrNull, Decl(typeGuardsNestedAssignments.ts, 5, 44)) + + x; +>x : Symbol(x, Decl(typeGuardsNestedAssignments.ts, 32, 7)) + } +} + +// Repro from #8851 + +const re = /./g +>re : Symbol(re, Decl(typeGuardsNestedAssignments.ts, 40, 5)) + +let match: RegExpExecArray | null +>match : Symbol(match, Decl(typeGuardsNestedAssignments.ts, 41, 3)) +>RegExpExecArray : Symbol(RegExpExecArray, Decl(lib.d.ts, --, --)) + +while ((match = re.exec("xxx")) != null) { +>match : Symbol(match, Decl(typeGuardsNestedAssignments.ts, 41, 3)) +>re.exec : Symbol(RegExp.exec, Decl(lib.d.ts, --, --)) +>re : Symbol(re, Decl(typeGuardsNestedAssignments.ts, 40, 5)) +>exec : Symbol(RegExp.exec, Decl(lib.d.ts, --, --)) + + const length = match[1].length + match[2].length +>length : Symbol(length, Decl(typeGuardsNestedAssignments.ts, 44, 9)) +>match[1].length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>match : Symbol(match, Decl(typeGuardsNestedAssignments.ts, 41, 3)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>match[2].length : Symbol(String.length, Decl(lib.d.ts, --, --)) +>match : Symbol(match, Decl(typeGuardsNestedAssignments.ts, 41, 3)) +>length : Symbol(String.length, Decl(lib.d.ts, --, --)) +} diff --git a/tests/baselines/reference/typeGuardsNestedAssignments.types b/tests/baselines/reference/typeGuardsNestedAssignments.types new file mode 100644 index 00000000000..566a39a9c23 --- /dev/null +++ b/tests/baselines/reference/typeGuardsNestedAssignments.types @@ -0,0 +1,155 @@ +=== tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts === + +class Foo { +>Foo : Foo + + x: string; +>x : string +} + +declare function getFooOrNull(): Foo | null; +>getFooOrNull : () => Foo | null +>Foo : Foo +>null : null + +declare function getStringOrNumberOrNull(): string | number | null; +>getStringOrNumberOrNull : () => string | number | null +>null : null + +function f1() { +>f1 : () => void + + let foo: Foo | null; +>foo : Foo | null +>Foo : Foo +>null : null + + if ((foo = getFooOrNull()) !== null) { +>(foo = getFooOrNull()) !== null : boolean +>(foo = getFooOrNull()) : Foo | null +>foo = getFooOrNull() : Foo | null +>foo : Foo | null +>getFooOrNull() : Foo | null +>getFooOrNull : () => Foo | null +>null : null + + foo; // Foo +>foo : Foo + } +} + +function f2() { +>f2 : () => void + + let foo1: Foo | null; +>foo1 : Foo | null +>Foo : Foo +>null : null + + let foo2: Foo | null; +>foo2 : Foo | null +>Foo : Foo +>null : null + + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { +>(foo1 = getFooOrNull(), foo2 = foo1) !== null : boolean +>(foo1 = getFooOrNull(), foo2 = foo1) : Foo | null +>foo1 = getFooOrNull(), foo2 = foo1 : Foo | null +>foo1 = getFooOrNull() : Foo | null +>foo1 : Foo | null +>getFooOrNull() : Foo | null +>getFooOrNull : () => Foo | null +>foo2 = foo1 : Foo | null +>foo2 : Foo | null +>foo1 : Foo | null +>null : null + + foo1; // Foo | null +>foo1 : Foo | null + + foo2; // Foo +>foo2 : Foo + } +} + +function f3() { +>f3 : () => void + + let obj: Object | null; +>obj : Object | null +>Object : Object +>null : null + + if ((obj = getFooOrNull()) instanceof Foo) { +>(obj = getFooOrNull()) instanceof Foo : boolean +>(obj = getFooOrNull()) : Foo | null +>obj = getFooOrNull() : Foo | null +>obj : Object | null +>getFooOrNull() : Foo | null +>getFooOrNull : () => Foo | null +>Foo : typeof Foo + + obj; +>obj : Foo + } +} + +function f4() { +>f4 : () => void + + let x: string | number | null; +>x : string | number | null +>null : null + + if (typeof (x = getStringOrNumberOrNull()) === "number") { +>typeof (x = getStringOrNumberOrNull()) === "number" : boolean +>typeof (x = getStringOrNumberOrNull()) : string +>(x = getStringOrNumberOrNull()) : string | number | null +>x = getStringOrNumberOrNull() : string | number | null +>x : string | number | null +>getStringOrNumberOrNull() : string | number | null +>getStringOrNumberOrNull : () => string | number | null +>"number" : string + + x; +>x : number + } +} + +// Repro from #8851 + +const re = /./g +>re : RegExp +>/./g : RegExp + +let match: RegExpExecArray | null +>match : RegExpExecArray | null +>RegExpExecArray : RegExpExecArray +>null : null + +while ((match = re.exec("xxx")) != null) { +>(match = re.exec("xxx")) != null : boolean +>(match = re.exec("xxx")) : RegExpExecArray | null +>match = re.exec("xxx") : RegExpExecArray | null +>match : RegExpExecArray | null +>re.exec("xxx") : RegExpExecArray | null +>re.exec : (string: string) => RegExpExecArray | null +>re : RegExp +>exec : (string: string) => RegExpExecArray | null +>"xxx" : string +>null : null + + const length = match[1].length + match[2].length +>length : number +>match[1].length + match[2].length : number +>match[1].length : number +>match[1] : string +>match : RegExpExecArray +>1 : number +>length : number +>match[2].length : number +>match[2] : string +>match : RegExpExecArray +>2 : number +>length : number +} diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt b/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt deleted file mode 100644 index dfcb8a598da..00000000000 --- a/tests/baselines/reference/typeGuardsWithInstanceOf.errors.txt +++ /dev/null @@ -1,14 +0,0 @@ -tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts(7,20): error TS2339: Property 'global' does not exist on type 'never'. - - -==== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts (1 errors) ==== - interface I { global: string; } - var result: I; - var result2: I; - - if (!(result instanceof RegExp)) { - result = result2; - } else if (!result.global) { - ~~~~~~ -!!! error TS2339: Property 'global' does not exist on type 'never'. - } \ No newline at end of file diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.symbols b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols new file mode 100644 index 00000000000..6d1667a6519 --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.symbols @@ -0,0 +1,26 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts === +interface I { global: string; } +>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) +>global : Symbol(I.global, Decl(typeGuardsWithInstanceOf.ts, 0, 13)) + +var result: I; +>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) +>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) + +var result2: I; +>result2 : Symbol(result2, Decl(typeGuardsWithInstanceOf.ts, 2, 3)) +>I : Symbol(I, Decl(typeGuardsWithInstanceOf.ts, 0, 0)) + +if (!(result instanceof RegExp)) { +>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) +>RegExp : Symbol(RegExp, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --)) + + result = result2; +>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) +>result2 : Symbol(result2, Decl(typeGuardsWithInstanceOf.ts, 2, 3)) + +} else if (!result.global) { +>result.global : Symbol(global, Decl(typeGuardsWithInstanceOf.ts, 0, 13), Decl(lib.d.ts, --, --)) +>result : Symbol(result, Decl(typeGuardsWithInstanceOf.ts, 1, 3)) +>global : Symbol(global, Decl(typeGuardsWithInstanceOf.ts, 0, 13), Decl(lib.d.ts, --, --)) +} diff --git a/tests/baselines/reference/typeGuardsWithInstanceOf.types b/tests/baselines/reference/typeGuardsWithInstanceOf.types new file mode 100644 index 00000000000..f54498f93c8 --- /dev/null +++ b/tests/baselines/reference/typeGuardsWithInstanceOf.types @@ -0,0 +1,31 @@ +=== tests/cases/conformance/expressions/typeGuards/typeGuardsWithInstanceOf.ts === +interface I { global: string; } +>I : I +>global : string + +var result: I; +>result : I +>I : I + +var result2: I; +>result2 : I +>I : I + +if (!(result instanceof RegExp)) { +>!(result instanceof RegExp) : boolean +>(result instanceof RegExp) : boolean +>result instanceof RegExp : boolean +>result : I +>RegExp : RegExpConstructor + + result = result2; +>result = result2 : I +>result : I +>result2 : I + +} else if (!result.global) { +>!result.global : boolean +>result.global : string & boolean +>result : I & RegExp +>global : string & boolean +} diff --git a/tests/baselines/reference/typeReferenceDirectives13.symbols b/tests/baselines/reference/typeReferenceDirectives13.symbols index 90a32b16341..396cde9c3b4 100644 --- a/tests/baselines/reference/typeReferenceDirectives13.symbols +++ b/tests/baselines/reference/typeReferenceDirectives13.symbols @@ -8,7 +8,7 @@ export interface A { x: () => typeof $ >x : Symbol(A.x, Decl(app.ts, 2, 20)) ->$ : Symbol($, Decl(app.ts, 1, 8)) +>$ : Symbol($, Decl(index.d.ts, 0, 11)) } === /ref.d.ts === diff --git a/tests/baselines/reference/typeReferenceDirectives5.symbols b/tests/baselines/reference/typeReferenceDirectives5.symbols index fbbb06d6834..16f7b4ce808 100644 --- a/tests/baselines/reference/typeReferenceDirectives5.symbols +++ b/tests/baselines/reference/typeReferenceDirectives5.symbols @@ -8,7 +8,7 @@ export interface A { x: typeof $; >x : Symbol(A.x, Decl(app.ts, 2, 20)) ->$ : Symbol($, Decl(app.ts, 1, 8)) +>$ : Symbol($, Decl(index.d.ts, 0, 11)) } === /ref.d.ts === diff --git a/tests/cases/compiler/asyncFunctionReturnType.ts b/tests/cases/compiler/asyncFunctionReturnType.ts new file mode 100644 index 00000000000..a1bcde38079 --- /dev/null +++ b/tests/cases/compiler/asyncFunctionReturnType.ts @@ -0,0 +1,10 @@ +// @target: ES6 +async function fAsync() { + // Without explicit type annotation, this is just an array. + return [1, true]; +} + +async function fAsyncExplicit(): Promise<[number, boolean]> { + // This is contextually typed as a tuple. + return [1, true]; +} diff --git a/tests/cases/compiler/disallowAsyncModifierInES5.ts b/tests/cases/compiler/disallowAsyncModifierInES5.ts new file mode 100644 index 00000000000..5417e678dce --- /dev/null +++ b/tests/cases/compiler/disallowAsyncModifierInES5.ts @@ -0,0 +1,5 @@ +// @target: es5 + +async function foo() { return 42; } // ERROR: Async functions are only available in ES6+ +let bar = async function () { return 42; } // OK, but should be an error +let baz = async () => 42; // OK, but should be an error \ No newline at end of file diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts new file mode 100644 index 00000000000..d48f50bfa50 --- /dev/null +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns.ts @@ -0,0 +1,30 @@ +// @Filename: declarations.d.ts +declare module "foo*baz" { + export function foo(s: string): void; +} +// Augmentations still work +declare module "foo*baz" { + export const baz: string; +} + +// Longest prefix wins +declare module "foos*" { + export const foos: string; +} + +declare module "*!text" { + const x: string; + export default x; +} + +// @Filename: user.ts +/// +import {foo, baz} from "foobarbaz"; +foo(baz); + +import {foos} from "foosball"; +foo(foos); + +// Works with relative file name +import fileText from "./file!text"; +foo(fileText); diff --git a/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts b/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts new file mode 100644 index 00000000000..76f9081906c --- /dev/null +++ b/tests/cases/conformance/ambient/ambientDeclarationsPatterns_tooManyAsterisks.ts @@ -0,0 +1 @@ +declare module "too*many*asterisks" { } diff --git a/tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts b/tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts new file mode 100644 index 00000000000..41e3ffe572e --- /dev/null +++ b/tests/cases/conformance/controlFlow/typeGuardsNestedAssignments.ts @@ -0,0 +1,47 @@ +// @strictNullChecks: true + +class Foo { + x: string; +} + +declare function getFooOrNull(): Foo | null; +declare function getStringOrNumberOrNull(): string | number | null; + +function f1() { + let foo: Foo | null; + if ((foo = getFooOrNull()) !== null) { + foo; // Foo + } +} + +function f2() { + let foo1: Foo | null; + let foo2: Foo | null; + if ((foo1 = getFooOrNull(), foo2 = foo1) !== null) { + foo1; // Foo | null + foo2; // Foo + } +} + +function f3() { + let obj: Object | null; + if ((obj = getFooOrNull()) instanceof Foo) { + obj; + } +} + +function f4() { + let x: string | number | null; + if (typeof (x = getStringOrNumberOrNull()) === "number") { + x; + } +} + +// Repro from #8851 + +const re = /./g +let match: RegExpExecArray | null + +while ((match = re.exec("xxx")) != null) { + const length = match[1].length + match[2].length +} \ No newline at end of file diff --git a/tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts b/tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts new file mode 100644 index 00000000000..a4ad5ca7719 --- /dev/null +++ b/tests/cases/conformance/expressions/binaryOperators/logicalAndOperator/logicalAndOperatorStrictMode.ts @@ -0,0 +1,82 @@ +// @strictNullChecks: true + +const a = [0]; +const s = ""; +const x = 0; +const b = false; +const v: void = undefined; +const u = undefined; +const n = null; +const z = s || x || u; + +const a1 = a && a; +const a2 = a && s; +const a3 = a && x; +const a4 = a && b; +const a5 = a && v; +const a6 = a && u; +const a7 = a && n; +const a8 = a && z; + +const s1 = s && a; +const s2 = s && s; +const s3 = s && x; +const s4 = s && b; +const s5 = s && v; +const s6 = s && u; +const s7 = s && n; +const s8 = s && z; + +const x1 = x && a; +const x2 = x && s; +const x3 = x && x; +const x4 = x && b; +const x5 = x && v; +const x6 = x && u; +const x7 = x && n; +const x8 = x && z; + +const b1 = b && a; +const b2 = b && s; +const b3 = b && x; +const b4 = b && b; +const b5 = b && v; +const b6 = b && u; +const b7 = b && n; +const b8 = b && z; + +const v1 = v && a; +const v2 = v && s; +const v3 = v && x; +const v4 = v && b; +const v5 = v && v; +const v6 = v && u; +const v7 = v && n; +const v8 = v && z; + +const u1 = u && a; +const u2 = u && s; +const u3 = u && x; +const u4 = u && b; +const u5 = u && v; +const u6 = u && u; +const u7 = u && n; +const u8 = u && z; + +const n1 = n && a; +const n2 = n && s; +const n3 = n && x; +const n4 = n && b; +const n5 = n && v; +const n6 = n && u; +const n7 = n && n; +const n8 = n && z; + +const z1 = z && a; +const z2 = z && s; +const z3 = z && x; +const z4 = z && b; +const z5 = z && v; +const z6 = z && u; +const z7 = z && n; +const z8 = z && z; \ No newline at end of file diff --git a/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts new file mode 100644 index 00000000000..85c003787fd --- /dev/null +++ b/tests/cases/conformance/expressions/typeGuards/typeGuardIntersectionTypes.ts @@ -0,0 +1,113 @@ +// @strictNullChecks: true + +interface X { + x: string; +} + +interface Y { + y: string; +} + +interface Z { + z: string; +} + +declare function isX(obj: any): obj is X; +declare function isY(obj: any): obj is Y; +declare function isZ(obj: any): obj is Z; + +function f1(obj: Object) { + if (isX(obj) || isY(obj) || isZ(obj)) { + obj; + } + if (isX(obj) && isY(obj) && isZ(obj)) { + obj; + } +} + +// Repro from #8911 + +// two interfaces +interface A { + a: string; +} + +interface B { + b: string; +} + +// a type guard for B +function isB(toTest: any): toTest is B { + return toTest && toTest.b; +} + +// a function that turns an A into an A & B +function union(a: A): A & B | null { + if (isB(a)) { + return a; + } else { + return null; + } +} + +// Repro from #9016 + +declare function log(s: string): void; + +// Supported beast features +interface Beast { wings?: boolean; legs?: number } +interface Legged { legs: number; } +interface Winged { wings: boolean; } + +// Beast feature detection via user-defined type guards +function hasLegs(x: Beast): x is Legged { return x && typeof x.legs === 'number'; } +function hasWings(x: Beast): x is Winged { return x && !!x.wings; } + +// Function to identify a given beast by detecting its features +function identifyBeast(beast: Beast) { + + // All beasts with legs + if (hasLegs(beast)) { + + // All winged beasts with legs + if (hasWings(beast)) { + if (beast.legs === 4) { + log(`pegasus - 4 legs, wings`); + } + else if (beast.legs === 2) { + log(`bird - 2 legs, wings`); + } + else { + log(`unknown - ${beast.legs} legs, wings`); + } + } + + // All non-winged beasts with legs + else { + log(`manbearpig - ${beast.legs} legs, no wings`); + } + } + + // All beasts without legs + else { + if (hasWings(beast)) { + log(`quetzalcoatl - no legs, wings`) + } + else { + log(`snake - no legs, no wings`) + } + } +} + +function beastFoo(beast: Object) { + if (hasWings(beast) && hasLegs(beast)) { + beast; // Winged & Legged + } + else { + beast; + } + + if (hasLegs(beast) && hasWings(beast)) { + beast; // Legged & Winged + } +} \ No newline at end of file diff --git a/tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts b/tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts new file mode 100644 index 00000000000..6ad35658623 --- /dev/null +++ b/tests/cases/conformance/externalModules/moduleResolutionWithExtensions.ts @@ -0,0 +1,28 @@ +// @traceResolution: true + +// @Filename: /src/a.ts +export default 0; + +// No extension: '.ts' added +// @Filename: /src/b.ts +import a from './a'; + +// Matching extension +// @Filename: /src/c.ts +import a from './a.ts'; + +// '.js' extension: stripped and replaced with '.ts' +// @Filename: /src/d.ts +import a from './a.js'; + +// @Filename: /src/jquery.d.ts +declare var x: number; +export default x; + +// No extension: '.d.ts' added +// @Filename: /src/jquery_user_1.ts +import j from "./jquery"; + +// '.js' extension: stripped and replaced with '.d.ts' +// @Filename: /src/jquery_user_1.ts +import j from "./jquery.js" diff --git a/tests/cases/fourslash/completionForStringLiteral.ts b/tests/cases/fourslash/completionForStringLiteral.ts new file mode 100644 index 00000000000..cdbe1589f73 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral.ts @@ -0,0 +1,15 @@ +/// + +////type Options = "Option 1" | "Option 2" | "Option 3"; +////var x: Options = "/*1*/Option 3"; +//// +////function f(a: Options) { }; +////f("/*2*/ + +goTo.marker('1'); +verify.completionListContains("Option 1"); +verify.memberListCount(3); + +goTo.marker('2'); +verify.completionListContains("Option 2"); +verify.memberListCount(3); diff --git a/tests/cases/fourslash/completionForStringLiteral2.ts b/tests/cases/fourslash/completionForStringLiteral2.ts new file mode 100644 index 00000000000..6f0768d6c6b --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral2.ts @@ -0,0 +1,20 @@ +/// + +////var o = { +//// foo() { }, +//// bar: 0, +//// "some other name": 1 +////}; +//// +////o["/*1*/bar"]; +////o["/*2*/ + +goTo.marker('1'); +verify.completionListContains("foo"); +verify.completionListAllowsNewIdentifier(); +verify.memberListCount(3); + +goTo.marker('2'); +verify.completionListContains("some other name"); +verify.completionListAllowsNewIdentifier(); +verify.memberListCount(3); diff --git a/tests/cases/fourslash/completionForStringLiteral3.ts b/tests/cases/fourslash/completionForStringLiteral3.ts new file mode 100644 index 00000000000..8c1a7cab2ed --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral3.ts @@ -0,0 +1,20 @@ +/// + +////declare function f(a: "A", b: number): void; +////declare function f(a: "B", b: number): void; +////declare function f(a: "C", b: number): void; +////declare function f(a: string, b: number): void; +//// +////f("/*1*/C", 2); +//// +////f("/*2*/ + +goTo.marker('1'); +verify.completionListContains("A"); +verify.completionListAllowsNewIdentifier(); +verify.memberListCount(3); + +goTo.marker('2'); +verify.completionListContains("A"); +verify.completionListAllowsNewIdentifier(); +verify.memberListCount(3); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 71e1c3cc5d7..540a6503138 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -52,13 +52,13 @@ declare module ts { None = 0, Block = 1, Smart = 2, - } + } interface OutputFile { name: string; writeByteOrderMark: boolean; text: string; - } + } } declare namespace FourSlashInterface { @@ -139,7 +139,7 @@ declare namespace FourSlashInterface { isValidBraceCompletionAtPostion(openingBrace?: string): void; } class verify extends verifyNegatable { - assertHasRanges(ranges: FourSlash.Range[]): void; + assertHasRanges(ranges: Range[]): void; caretAtMarker(markerName?: string): void; indentationIs(numberOfSpaces: number): void; indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle?: ts.IndentStyle): void; diff --git a/tests/cases/fourslash/quickInfoMeaning.ts b/tests/cases/fourslash/quickInfoMeaning.ts new file mode 100644 index 00000000000..51b17337486 --- /dev/null +++ b/tests/cases/fourslash/quickInfoMeaning.ts @@ -0,0 +1,67 @@ +/// + +// Testing that quickInfo gets information with a corresponding meaning: values to values, types to types. +// For quick info purposes, we don't resolve past aliases. +// However, when we have an alias for a type, the quickInfo for a value with the same should skip the alias, and vice versa. +// goToDefinition should work the same way. + +// @Filename: foo.d.ts +////declare const /*foo_value_declaration*/foo: number; +////declare module "foo_module" { +//// interface I { x: number; y: number } +//// export = I; +////} + +// @Filename: foo_user.ts +/////// +/////*foo_type_declaration*/import foo = require("foo_module"); +////const x = foo/*foo_value*/; +////const i: foo/*foo_type*/ = { x: 1, y: 2 }; + +verify.numberOfErrorsInCurrentFile(0); + +verify.navigationItemsListCount(2, "foo", "exact"); +verify.navigationItemsListContains("foo", "alias", "foo", "exact"); +verify.navigationItemsListContains("foo", "const", "foo", "exact"); + +goTo.marker("foo_value"); +verify.quickInfoIs("const foo: number"); +goTo.definition(); +verify.caretAtMarker("foo_value_declaration"); + +goTo.marker("foo_type"); +verify.quickInfoIs("import foo = require(\"foo_module\")"); +goTo.definition(); +verify.caretAtMarker("foo_type_declaration"); + + +// Above tested for global const and imported interface. Now test with global interface and imported const. + + +// @Filename: bar.d.ts +/////*bar_type_declaration*/declare interface bar { x: number; y: number } +////declare module "bar_module" { +//// const x: number; +//// export = x; +////} + +// @Filename: bar_user.ts +/////// +/////*bar_value_declaration*/import bar = require("bar_module"); +////const x = bar/*bar_value*/; +////const i: bar/*bar_type*/ = { x: 1, y: 2 }; + +verify.numberOfErrorsInCurrentFile(0); +verify.navigationItemsListCount(2, "bar", "exact"); +verify.navigationItemsListContains("bar", "alias", "bar", "exact"); +verify.navigationItemsListContains("bar", "interface", "bar", "exact"); + +goTo.marker("bar_value"); +verify.quickInfoIs("import bar = require(\"bar_module\")"); +goTo.definition(); +verify.caretAtMarker("bar_value_declaration"); + +goTo.marker("bar_type"); +verify.quickInfoIs("interface bar"); +goTo.definition(); +verify.caretAtMarker("bar_type_declaration"); diff --git a/tests/cases/projects/NoModule/decl.ts b/tests/cases/projects/NoModule/decl.ts index a8a29852ac2..064f777c551 100644 --- a/tests/cases/projects/NoModule/decl.ts +++ b/tests/cases/projects/NoModule/decl.ts @@ -1,4 +1,4 @@ -import modErr = require("./foo/bar.js"); +import modErr = require("./foo/bar.tx"); import modErr1 = require("baz"); import modErr2 = require("./baz"); diff --git a/tests/cases/unittests/transpile.ts b/tests/cases/unittests/transpile.ts index 904cc435c44..a4b1416ed29 100644 --- a/tests/cases/unittests/transpile.ts +++ b/tests/cases/unittests/transpile.ts @@ -7,17 +7,21 @@ namespace ts { options?: TranspileOptions; expectedOutput?: string; expectedDiagnosticCodes?: number[]; + expectedDiagnosticTexts?: string[]; } - function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) { - if (!expectedDiagnosticCodes) { - return; + function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes: number[] = [], expectedDiagnosticTexts?: string[]) { + const n = expectedDiagnosticCodes.length; + if (expectedDiagnosticTexts) { + assert.equal(n, expectedDiagnosticTexts.length); } - - for (let i = 0; i < expectedDiagnosticCodes.length; i++) { - assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`); - } - assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected"); + for (let i = 0; i < n; i++) { + assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expected diagnostic.`); + if (expectedDiagnosticTexts) { + assert.equal(expectedDiagnosticTexts[i], diagnostics[i] && diagnostics[i].messageText); + } + }; + assert.equal(diagnostics.length, n, "Resuting diagnostics count does not match expected"); } function test(input: string, testSettings: TranspileTestSettings): void { @@ -26,7 +30,7 @@ namespace ts { if (!transpileOptions.compilerOptions) { transpileOptions.compilerOptions = {}; } - if (transpileOptions.compilerOptions.newLine === undefined) { // + if (transpileOptions.compilerOptions.newLine === undefined) { // use \r\n as default new line transpileOptions.compilerOptions.newLine = ts.NewLineKind.CarriageReturnLineFeed; } @@ -36,7 +40,7 @@ namespace ts { transpileOptions.reportDiagnostics = true; const transpileModuleResult = transpileModule(input, transpileOptions); - checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes); + checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes, testSettings.expectedDiagnosticTexts); if (testSettings.expectedOutput !== undefined) { assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput); @@ -45,7 +49,7 @@ namespace ts { if (canUseOldTranspile) { const diagnostics: Diagnostic[] = []; const transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName); - checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes); + checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes, testSettings.expectedDiagnosticTexts); if (testSettings.expectedOutput) { assert.equal(transpileResult, testSettings.expectedOutput); } @@ -292,13 +296,37 @@ var x = 0;`, const output = `"use strict";\nvar a = 10;\n`; test(input, { expectedOutput: output, - options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true }, - expectedDiagnosticCodes: [] + options: { compilerOptions: { newLine: NewLineKind.LineFeed, module: ModuleKind.CommonJS }, fileName: "input.js", reportDiagnostics: true } }); }); it("Supports urls in file name", () => { test("var x", { expectedOutput: `"use strict";\r\nvar x;\r\n`, options: { fileName: "http://somewhere/directory//directory2/file.ts" } }); }); + + describe("String values for enums", () => { + it("Accepts strings instead of enum values", () => { + test(`export const x = 0`, { + options: { + compilerOptions: { + module: "es6", + // Capitalization and spaces ignored + target: " Es6 " + } + }, + expectedOutput: "export const x = 0;\r\n" + }); + }); + + it("Fails on bad value", () => { + for (const value in [123, {}, ""]) { + test(``, { + options: { compilerOptions: { module: value } }, + expectedDiagnosticCodes: [6046], + expectedDiagnosticTexts: ["Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'"] + }); + } + }); + }); }); }