diff --git a/.travis.yml b/.travis.yml
index 989924dc32c..bfc07e2b510 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,9 +7,33 @@ node_js:
sudo: false
-os:
- - linux
- - osx
+env:
+ - workerCount=3
matrix:
fast_finish: true
+ include:
+ - os: osx
+ node_js: stable
+ osx_image: xcode7.3
+ env: workerCount=2
+ allow_failures:
+ - os: osx
+
+branches:
+ only:
+ - master
+ - transforms
+
+install:
+ - npm uninstall typescript
+ - npm uninstall tslint
+ - npm install
+ - npm update
+
+cache:
+ directories:
+ - node_modules
+
+git:
+ depth: 1
diff --git a/Gulpfile.ts b/Gulpfile.ts
index 57da2b62aa8..1b902562832 100644
--- a/Gulpfile.ts
+++ b/Gulpfile.ts
@@ -17,7 +17,7 @@ declare module "gulp-typescript" {
stripInternal?: boolean;
types?: string[];
}
- interface CompileStream extends NodeJS.ReadWriteStream {} // Either gulp or gulp-typescript has some odd typings which don't reflect reality, making this required
+ interface CompileStream extends NodeJS.ReadWriteStream { } // Either gulp or gulp-typescript has some odd typings which don't reflect reality, making this required
}
import * as insert from "gulp-insert";
import * as sourcemaps from "gulp-sourcemaps";
@@ -34,7 +34,7 @@ import through2 = require("through2");
import merge2 = require("merge2");
import intoStream = require("into-stream");
import * as os from "os";
-import Linter = require("tslint");
+import fold = require("travis-fold");
const gulp = helpMaker(originalGulp);
const mochaParallel = require("./scripts/mocha-parallel.js");
const {runTestsInParallel} = mochaParallel;
@@ -59,14 +59,13 @@ const cmdLineOptions = minimist(process.argv.slice(2), {
browser: process.env.browser || process.env.b || "IE",
tests: process.env.test || process.env.tests || process.env.t,
light: process.env.light || false,
- port: process.env.port || process.env.p || "8888",
reporter: process.env.reporter || process.env.r,
lint: process.env.lint || true,
files: process.env.f || process.env.file || process.env.files || "",
}
});
-function exec(cmd: string, args: string[], complete: () => void = (() => {}), error: (e: any, status: number) => void = (() => {})) {
+function exec(cmd: string, args: string[], complete: () => void = (() => { }), error: (e: any, status: number) => void = (() => { })) {
console.log(`${cmd} ${args.join(" ")}`);
// TODO (weswig): Update child_process types to add windowsVerbatimArguments to the type definition
const subshellFlag = isWin ? "/c" : "-c";
@@ -117,12 +116,12 @@ const es2015LibrarySources = [
];
const es2015LibrarySourceMap = es2015LibrarySources.map(function(source) {
- return { target: "lib." + source, sources: ["header.d.ts", source] };
+ return { target: "lib." + source, sources: ["header.d.ts", source] };
});
-const es2016LibrarySource = [ "es2016.array.include.d.ts" ];
+const es2016LibrarySource = ["es2016.array.include.d.ts"];
-const es2016LibrarySourceMap = es2016LibrarySource.map(function (source) {
+const es2016LibrarySourceMap = es2016LibrarySource.map(function(source) {
return { target: "lib." + source, sources: ["header.d.ts", source] };
});
@@ -131,38 +130,38 @@ const es2017LibrarySource = [
"es2017.sharedmemory.d.ts"
];
-const es2017LibrarySourceMap = es2017LibrarySource.map(function (source) {
+const es2017LibrarySourceMap = es2017LibrarySource.map(function(source) {
return { target: "lib." + source, sources: ["header.d.ts", source] };
});
const hostsLibrarySources = ["dom.generated.d.ts", "webworker.importscripts.d.ts", "scripthost.d.ts"];
const librarySourceMap = [
- // Host library
- { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] },
- { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] },
- { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] },
- { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] },
+ // Host library
+ { target: "lib.dom.d.ts", sources: ["header.d.ts", "dom.generated.d.ts"] },
+ { target: "lib.dom.iterable.d.ts", sources: ["header.d.ts", "dom.iterable.d.ts"] },
+ { target: "lib.webworker.d.ts", sources: ["header.d.ts", "webworker.generated.d.ts"] },
+ { target: "lib.scripthost.d.ts", sources: ["header.d.ts", "scripthost.d.ts"] },
- // JavaScript library
- { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] },
- { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] },
- { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] },
- { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] },
+ // JavaScript library
+ { target: "lib.es5.d.ts", sources: ["header.d.ts", "es5.d.ts"] },
+ { target: "lib.es2015.d.ts", sources: ["header.d.ts", "es2015.d.ts"] },
+ { target: "lib.es2016.d.ts", sources: ["header.d.ts", "es2016.d.ts"] },
+ { target: "lib.es2017.d.ts", sources: ["header.d.ts", "es2017.d.ts"] },
- // JavaScript + all host library
- { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) },
- { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }
+ // JavaScript + all host library
+ { target: "lib.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(hostsLibrarySources) },
+ { target: "lib.es6.d.ts", sources: ["header.d.ts", "es5.d.ts"].concat(es2015LibrarySources, hostsLibrarySources, "dom.iterable.d.ts") }
].concat(es2015LibrarySourceMap, es2016LibrarySourceMap, es2017LibrarySourceMap);
-const libraryTargets = librarySourceMap.map(function (f) {
+const libraryTargets = librarySourceMap.map(function(f) {
return path.join(builtLocalDirectory, f.target);
});
for (const i in libraryTargets) {
const entry = librarySourceMap[i];
const target = libraryTargets[i];
- const sources = [copyright].concat(entry.sources.map(function (s) {
+ const sources = [copyright].concat(entry.sources.map(function(s) {
return path.join(libraryDirectory, s);
}));
gulp.task(target, false, [], function() {
@@ -392,7 +391,7 @@ gulp.task(servicesFile, false, ["lib", "generate-diagnostics"], () => {
.pipe(sourcemaps.init())
.pipe(tsc(servicesProject));
const completedJs = js.pipe(prependCopyright())
- .pipe(sourcemaps.write("."));
+ .pipe(sourcemaps.write("."));
const completedDts = dts.pipe(prependCopyright(/*outputCopyright*/true))
.pipe(insert.transform((contents, file) => {
file.path = standaloneDefinitionsFile;
@@ -435,22 +434,22 @@ const tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverli
gulp.task(tsserverLibraryFile, false, [servicesFile], (done) => {
const serverLibraryProject = tsc.createProject("src/server/tsconfig.library.json", getCompilerSettings({}, /*useBuiltCompiler*/ true));
- const {js, dts}: {js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream} = serverLibraryProject.src()
+ const {js, dts}: { js: NodeJS.ReadableStream, dts: NodeJS.ReadableStream } = serverLibraryProject.src()
.pipe(sourcemaps.init())
.pipe(newer(tsserverLibraryFile))
.pipe(tsc(serverLibraryProject));
return merge2([
js.pipe(prependCopyright())
- .pipe(sourcemaps.write("."))
- .pipe(gulp.dest(builtLocalDirectory)),
+ .pipe(sourcemaps.write("."))
+ .pipe(gulp.dest(builtLocalDirectory)),
dts.pipe(prependCopyright())
- .pipe(gulp.dest(builtLocalDirectory))
+ .pipe(gulp.dest(builtLocalDirectory))
]);
});
gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile]);
-gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON]);
+gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile]);
gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]);
@@ -477,7 +476,7 @@ gulp.task(specMd, false, [word2mdJs], (done) => {
const specMDFullPath = path.resolve(specMd);
const cmd = "cscript //nologo " + word2mdJs + " \"" + specWordFullPath + "\" " + "\"" + specMDFullPath + "\"";
console.log(cmd);
- cp.exec(cmd, function () {
+ cp.exec(cmd, function() {
done();
});
});
@@ -493,18 +492,18 @@ gulp.task("dontUseDebugMode", false, [], (done) => { useDebugMode = false; done(
gulp.task("VerifyLKG", false, [], () => {
const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets);
- const missingFiles = expectedFiles.filter(function (f) {
+ const missingFiles = expectedFiles.filter(function(f) {
return !fs.existsSync(f);
});
if (missingFiles.length > 0) {
throw new Error("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory +
- ". The following files are missing:\n" + missingFiles.join("\n"));
+ ". The following files are missing:\n" + missingFiles.join("\n"));
}
// Copy all the targets into the LKG directory
return gulp.src(expectedFiles).pipe(gulp.dest(LKGDirectory));
});
-gulp.task("LKGInternal", false, ["lib", "local", "lssl"]);
+gulp.task("LKGInternal", false, ["lib", "local"]);
gulp.task("LKG", "Makes a new LKG out of the built js files", ["clean", "dontUseDebugMode"], () => {
return runSequence("LKGInternal", "VerifyLKG");
@@ -532,8 +531,6 @@ const localRwcBaseline = path.join(internalTests, "baselines/rwc/local");
const refRwcBaseline = path.join(internalTests, "baselines/rwc/reference");
const localTest262Baseline = path.join(internalTests, "baselines/test262/local");
-const refTest262Baseline = path.join(internalTests, "baselines/test262/reference");
-
gulp.task("tests", "Builds the test infrastructure using the built compiler", [run]);
gulp.task("tests-debug", "Builds the test sources and automation in debug mode", () => {
@@ -628,7 +625,7 @@ function runConsoleTests(defaultReporter: string, runInParallel: boolean, done:
}
args.push(run);
setNodeEnvToDevelopment();
- runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) {
+ 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
del(taskConfigsFolder).then(() => {
if (!err) {
@@ -680,7 +677,7 @@ gulp.task("runtests",
["build-rules", "tests"],
(done) => {
runConsoleTests("mocha-fivemat-progress-reporter", /*runInParallel*/ false, done);
-});
+ });
const nodeServerOutFile = "tests/webTestServer.js";
const nodeServerInFile = "tests/webTestServer.ts";
@@ -710,7 +707,7 @@ gulp.task("browserify", "Runs browserify on run.js to produce a file suitable fo
const originalMap = file.sourceMap;
const prebundledContent = file.contents.toString();
// Make paths absolute to help sorcery deal with all the terrible paths being thrown around
- originalMap.sources = originalMap.sources.map(s => path.resolve(s));
+ originalMap.sources = originalMap.sources.map(s => path.resolve("src", s));
// intoStream (below) makes browserify think the input file is named this, so this is what it puts in the sourcemap
originalMap.file = "built/local/_stream_0.js";
@@ -766,7 +763,7 @@ function writeTestConfigFile(tests: string, light: boolean, taskConfigsFolder?:
}
-gulp.task("runtests-browser", "Runs the tests using the built run.js file like 'gulp runtests'. Syntax is gulp runtests-browser. Additional optional parameters --tests=[regex], --port=, --browser=[chrome|IE]", ["browserify", nodeServerOutFile], (done) => {
+gulp.task("runtests-browser", "Runs the tests using the built run.js file like 'gulp runtests'. Syntax is gulp runtests-browser. Additional optional parameters --tests=[regex], --browser=[chrome|IE]", ["browserify", nodeServerOutFile], (done) => {
cleanTestDirs((err) => {
if (err) { console.error(err); done(err); process.exit(1); }
host = "node";
@@ -781,9 +778,6 @@ gulp.task("runtests-browser", "Runs the tests using the built run.js file like '
}
const args = [nodeServerOutFile];
- if (cmdLineOptions["port"]) {
- args.push(cmdLineOptions["port"]);
- }
if (cmdLineOptions["browser"]) {
args.push(cmdLineOptions["browser"]);
}
@@ -815,32 +809,36 @@ gulp.task("diff-rwc", "Diffs the RWC baselines using the diff tool specified by
exec(getDiffTool(), [refRwcBaseline, localRwcBaseline], done, done);
});
+gulp.task("baseline-accept", "Makes the most recent test results the new baseline, overwriting the old baseline", () => {
+ return baselineAccept("");
+});
+
+function baselineAccept(subfolder = "") {
+ return merge2(baselineCopy(subfolder), baselineDelete(subfolder));
+}
+
+function baselineCopy(subfolder = "") {
+ return gulp.src([`tests/baselines/local/${subfolder}/**`, `!tests/baselines/local/${subfolder}/**/*.delete`])
+ .pipe(gulp.dest(refBaseline));
+}
+
+function baselineDelete(subfolder = "") {
+ return gulp.src(["tests/baselines/local/**/*.delete"])
+ .pipe(insert.transform((content, fileObj) => {
+ const target = path.join(refBaseline, fileObj.relative.substr(0, fileObj.relative.length - ".delete".length));
+ del.sync(target);
+ del.sync(fileObj.path);
+ return "";
+ }));
+}
-gulp.task("baseline-accept", "Makes the most recent test results the new baseline, overwriting the old baseline", (done) => {
- const softAccept = cmdLineOptions["soft"];
- if (!softAccept) {
- del(refBaseline).then(() => {
- fs.renameSync(localBaseline, refBaseline);
- done();
- }, done);
- }
- else {
- gulp.src(localBaseline)
- .pipe(gulp.dest(refBaseline))
- .on("end", () => {
- del(path.join(refBaseline, "local")).then(() => done(), done);
- });
- }
-});
gulp.task("baseline-accept-rwc", "Makes the most recent rwc test results the new baseline, overwriting the old baseline", () => {
- return del(refRwcBaseline).then(() => {
- fs.renameSync(localRwcBaseline, refRwcBaseline);
- });
+ return baselineAccept("rwc");
});
+
+
gulp.task("baseline-accept-test262", "Makes the most recent test262 test results the new baseline, overwriting the old baseline", () => {
- return del(refTest262Baseline).then(() => {
- fs.renameSync(localTest262Baseline, refTest262Baseline);
- });
+ return baselineAccept("test262");
});
@@ -918,56 +916,19 @@ gulp.task("update-sublime", "Updates the sublime plugin's tsserver", ["local", s
return gulp.src([serverFile, serverFile + ".map"]).pipe(gulp.dest("../TypeScript-Sublime-Plugin/tsserver/"));
});
-
-const tslintRuleDir = "scripts/tslint";
-const tslintRules = [
- "nextLineRule",
- "preferConstRule",
- "booleanTriviaRule",
- "typeOperatorSpacingRule",
- "noInOperatorRule",
- "noIncrementDecrementRule",
- "objectLiteralSurroundingSpaceRule",
-];
-const tslintRulesFiles = tslintRules.map(function(p) {
- return path.join(tslintRuleDir, p + ".ts");
+gulp.task("build-rules", "Compiles tslint rules to js", () => {
+ const settings: tsc.Settings = getCompilerSettings({ module: "commonjs" }, /*useBuiltCompiler*/ false);
+ const dest = path.join(builtLocalDirectory, "tslint");
+ return gulp.src("scripts/tslint/**/*.ts")
+ .pipe(newer({
+ dest,
+ ext: ".js"
+ }))
+ .pipe(sourcemaps.init())
+ .pipe(tsc(settings))
+ .pipe(sourcemaps.write("."))
+ .pipe(gulp.dest(dest));
});
-const tslintRulesOutFiles = tslintRules.map(function(p, i) {
- const pathname = path.join(builtLocalDirectory, "tslint", p + ".js");
- gulp.task(pathname, false, [], () => {
- const settings: tsc.Settings = getCompilerSettings({ module: "commonjs" }, /*useBuiltCompiler*/ false);
- return gulp.src(tslintRulesFiles[i])
- .pipe(newer(pathname))
- .pipe(sourcemaps.init())
- .pipe(tsc(settings))
- .pipe(sourcemaps.write("."))
- .pipe(gulp.dest(path.join(builtLocalDirectory, "tslint")));
- });
- return pathname;
-});
-
-gulp.task("build-rules", "Compiles tslint rules to js", tslintRulesOutFiles);
-
-
-function getLinterOptions() {
- return {
- configuration: require("./tslint.json"),
- formatter: "prose",
- formattersDirectory: undefined,
- rulesDirectory: "built/local/tslint"
- };
-}
-
-function lintFileContents(options, path, contents) {
- const ll = new Linter(path, contents, options);
- console.log("Linting '" + path + "'.");
- return ll.lint();
-}
-
-function lintFile(options, path) {
- const contents = fs.readFileSync(path, "utf8");
- return lintFileContents(options, path, contents);
-}
const lintTargets = [
"Gulpfile.ts",
@@ -977,29 +938,75 @@ const lintTargets = [
"src/server/**/*.ts",
"scripts/tslint/**/*.ts",
"src/services/**/*.ts",
+ "tests/*.ts", "tests/webhost/*.ts" // Note: does *not* descend recursively
];
+function sendNextFile(files: {path: string}[], child: cp.ChildProcess, callback: (failures: number) => void, failures: number) {
+ const file = files.pop();
+ if (file) {
+ console.log(`Linting '${file.path}'.`);
+ child.send({ kind: "file", name: file.path });
+ }
+ else {
+ child.send({ kind: "close" });
+ callback(failures);
+ }
+}
+
+function spawnLintWorker(files: {path: string}[], callback: (failures: number) => void) {
+ const child = cp.fork("./scripts/parallel-lint");
+ let failures = 0;
+ child.on("message", function(data) {
+ switch (data.kind) {
+ case "result":
+ if (data.failures > 0) {
+ failures += data.failures;
+ console.log(data.output);
+ }
+ sendNextFile(files, child, callback, failures);
+ break;
+ case "error":
+ console.error(data.error);
+ failures++;
+ sendNextFile(files, child, callback, failures);
+ break;
+ }
+ });
+ sendNextFile(files, child, callback, failures);
+}
gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are: --f[iles]=regex", ["build-rules"], () => {
const fileMatcher = RegExp(cmdLineOptions["files"]);
- const lintOptions = getLinterOptions();
- let failed = 0;
- return gulp.src(lintTargets)
- .pipe(insert.transform((contents, file) => {
- if (!fileMatcher.test(file.path)) return contents;
- const result = lintFile(lintOptions, file.path);
- if (result.failureCount > 0) {
- console.log(result.output);
- failed += result.failureCount;
+ if (fold.isTravis()) console.log(fold.start("lint"));
+
+ let files: {stat: fs.Stats, path: string}[] = [];
+ return gulp.src(lintTargets, { read: false })
+ .pipe(through2.obj((chunk, enc, cb) => {
+ files.push(chunk);
+ cb();
+ }, (cb) => {
+ files = files.filter(file => fileMatcher.test(file.path)).sort((filea, fileb) => filea.stat.size - fileb.stat.size);
+ const workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length;
+ for (let i = 0; i < workerCount; i++) {
+ spawnLintWorker(files, finished);
}
- return contents; // TODO (weswig): Automatically apply fixes? :3
- }))
- .on("end", () => {
- if (failed > 0) {
- console.error("Linter errors.");
- process.exit(1);
+
+ let completed = 0;
+ let failures = 0;
+ function finished(fails) {
+ completed++;
+ failures += fails;
+ if (completed === workerCount) {
+ if (fold.isTravis()) console.log(fold.end("lint"));
+ if (failures > 0) {
+ throw new Error(`Linter errors: ${failures}`);
+ }
+ else {
+ cb();
+ }
+ }
}
- });
+ }));
});
diff --git a/Jakefile.js b/Jakefile.js
index 3ccca833333..441f6aef4f9 100644
--- a/Jakefile.js
+++ b/Jakefile.js
@@ -4,7 +4,7 @@ var fs = require("fs");
var os = require("os");
var path = require("path");
var child_process = require("child_process");
-var Linter = require("tslint");
+var fold = require("travis-fold");
var runTestsInParallel = require("./scripts/mocha-parallel").runTestsInParallel;
// Variables
@@ -32,6 +32,28 @@ if (process.env.path !== undefined) {
process.env.PATH = nodeModulesPathPrefix + process.env.PATH;
}
+function toNs(diff) {
+ return diff[0] * 1e9 + diff[1];
+}
+
+function mark() {
+ if (!fold.isTravis()) return;
+ var stamp = process.hrtime();
+ var id = Math.floor(Math.random() * 0xFFFFFFFF).toString(16);
+ console.log("travis_time:start:" + id + "\r");
+ return {
+ stamp: stamp,
+ id: id
+ };
+}
+
+function measure(marker) {
+ if (!fold.isTravis()) return;
+ var diff = process.hrtime(marker.stamp);
+ var total = [marker.stamp[0] + diff[0], marker.stamp[1] + diff[1]];
+ console.log("travis_time:end:" + marker.id + ":start=" + toNs(marker.stamp) + ",finish=" + toNs(total) + ",duration=" + toNs(diff) + "\r");
+}
+
var compilerSources = [
"core.ts",
"performance.ts",
@@ -158,7 +180,8 @@ var harnessSources = harnessCoreSources.concat([
"convertCompilerOptionsFromJson.ts",
"convertTypingOptionsFromJson.ts",
"tsserverProjectSystem.ts",
- "matchFiles.ts"
+ "matchFiles.ts",
+ "initializeTSConfig.ts",
].map(function (f) {
return path.join(unittestsDirectory, f);
})).concat([
@@ -285,6 +308,7 @@ var builtLocalCompiler = path.join(builtLocalDirectory, compilerFilename);
*/
function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts, callback) {
file(outFile, prereqs, function() {
+ var startCompileTime = mark();
opts = opts || {};
var compilerPath = useBuiltCompiler ? builtLocalCompiler : LKGCompiler;
var options = "--noImplicitAny --noImplicitThis --noEmitOnError --types "
@@ -361,11 +385,13 @@ function compileFile(outFile, sources, prereqs, prefixes, useBuiltCompiler, opts
callback();
}
+ measure(startCompileTime);
complete();
});
ex.addListener("error", function() {
fs.unlinkSync(outFile);
fail("Compilation of " + outFile + " unsuccessful");
+ measure(startCompileTime);
});
ex.run();
}, {async: true});
@@ -551,7 +577,7 @@ var tsserverLibraryDefinitionFile = path.join(builtLocalDirectory, "tsserverlibr
compileFile(
tsserverLibraryFile,
languageServiceLibrarySources,
- [builtLocalDirectory, copyright].concat(languageServiceLibrarySources),
+ [builtLocalDirectory, copyright, builtLocalCompiler].concat(languageServiceLibrarySources).concat(libraryTargets),
/*prefixes*/ [copyright],
/*useBuiltCompiler*/ true,
{ noOutFile: false, generateDeclarations: true });
@@ -560,9 +586,19 @@ compileFile(
desc("Builds language service server library");
task("lssl", [tsserverLibraryFile, tsserverLibraryDefinitionFile]);
+desc("Emit the start of the build fold");
+task("build-fold-start", [] , function() {
+ if (fold.isTravis()) console.log(fold.start("build"));
+});
+
+desc("Emit the end of the build fold");
+task("build-fold-end", [] , function() {
+ if (fold.isTravis()) console.log(fold.end("build"));
+});
+
// Local target to build the compiler and services
desc("Builds the full compiler and services");
-task("local", ["generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON]);
+task("local", ["build-fold-start", "generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, builtGeneratedDiagnosticMessagesJSON, "lssl", "build-fold-end"]);
// Local target to build only tsc.js
desc("Builds only the compiler");
@@ -617,7 +653,7 @@ task("generate-spec", [specMd]);
// Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory
desc("Makes a new LKG out of the built js files");
-task("LKG", ["clean", "release", "local", "lssl"].concat(libraryTargets), function() {
+task("LKG", ["clean", "release", "local"].concat(libraryTargets), function() {
var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile].concat(libraryTargets);
var missingFiles = expectedFiles.filter(function (f) {
return !fs.existsSync(f);
@@ -645,7 +681,7 @@ var run = path.join(builtLocalDirectory, "run.js");
compileFile(
/*outFile*/ run,
/*source*/ harnessSources,
- /*prereqs*/ [builtLocalDirectory, tscFile].concat(libraryTargets).concat(harnessSources),
+ /*prereqs*/ [builtLocalDirectory, tscFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources),
/*prefixes*/ [],
/*useBuiltCompiler:*/ true,
/*opts*/ { inlineSourceMap: true, types: ["node", "mocha", "chai"] });
@@ -758,6 +794,7 @@ function runConsoleTests(defaultReporter, runInParallel) {
// timeout normally isn't necessary but Travis-CI has been timing out on compiler baselines occasionally
// default timeout is 2sec which really should be enough, but maybe we just need a small amount longer
if(!runInParallel) {
+ var startTime = mark();
tests = tests ? ' -g "' + tests + '"' : '';
var cmd = "mocha" + (debug ? " --debug-brk" : "") + " -R " + reporter + tests + colors + bail + ' -t ' + testTimeout + ' ' + run;
console.log(cmd);
@@ -766,10 +803,12 @@ function runConsoleTests(defaultReporter, runInParallel) {
process.env.NODE_ENV = "development";
exec(cmd, function () {
process.env.NODE_ENV = savedNodeEnv;
+ measure(startTime);
runLinter();
finish();
}, function(e, status) {
process.env.NODE_ENV = savedNodeEnv;
+ measure(startTime);
finish(status);
});
@@ -777,9 +816,10 @@ function runConsoleTests(defaultReporter, runInParallel) {
else {
var savedNodeEnv = process.env.NODE_ENV;
process.env.NODE_ENV = "development";
+ var startTime = mark();
runTestsInParallel(taskConfigsFolder, run, { testTimeout: testTimeout, noColors: colors === " --no-colors " }, function (err) {
process.env.NODE_ENV = savedNodeEnv;
-
+ measure(startTime);
// last worker clean everything and runs linter in case if there were no errors
deleteTemporaryProjectOutput();
jake.rmRf(taskConfigsFolder);
@@ -847,11 +887,10 @@ task("browserify", ["tests", builtLocalDirectory, nodeServerOutFile], function()
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]");
+desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], browser=[chrome|IE]");
task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function() {
cleanTestDirs();
host = "node";
- port = process.env.port || process.env.p || '8888';
browser = process.env.browser || process.env.b || "IE";
tests = process.env.test || process.env.tests || process.env.t;
var light = process.env.light || false;
@@ -864,7 +903,7 @@ task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFi
}
tests = tests ? tests : '';
- var cmd = host + " tests/webTestServer.js " + port + " " + browser + " " + JSON.stringify(tests);
+ var cmd = host + " tests/webTestServer.js " + browser + " " + JSON.stringify(tests);
console.log(cmd);
exec(cmd);
}, {async: true});
@@ -899,16 +938,16 @@ task("tests-debug", ["setDebugMode", "tests"]);
// Makes the test results the new baseline
desc("Makes the most recent test results the new baseline, overwriting the old baseline");
task("baseline-accept", function(hardOrSoft) {
- if (!hardOrSoft || hardOrSoft === "hard") {
- jake.rmRf(refBaseline);
- fs.renameSync(localBaseline, refBaseline);
- }
- else if (hardOrSoft === "soft") {
- var files = jake.readdirR(localBaseline);
- for (var i in files) {
+ var files = jake.readdirR(localBaseline);
+ var deleteEnding = '.delete';
+ for (var i in files) {
+ if (files[i].substr(files[i].length - deleteEnding.length) === deleteEnding) {
+ var filename = path.basename(files[i]);
+ filename = filename.substr(0, filename.length - deleteEnding.length);
+ fs.unlink(path.join(refBaseline, filename));
+ } else {
jake.cpR(files[i], refBaseline);
}
- jake.rmRf(path.join(refBaseline, "local"));
}
});
@@ -990,6 +1029,7 @@ var tslintRules = [
"noInOperatorRule",
"noIncrementDecrementRule",
"objectLiteralSurroundingSpaceRule",
+ "noTypeAssertionWhitespaceRule"
];
var tslintRulesFiles = tslintRules.map(function(p) {
return path.join(tslintRuleDir, p + ".ts");
@@ -998,41 +1038,21 @@ var tslintRulesOutFiles = tslintRules.map(function(p) {
return path.join(builtLocalDirectory, "tslint", p + ".js");
});
desc("Compiles tslint rules to js");
-task("build-rules", tslintRulesOutFiles);
+task("build-rules", ["build-rules-start"].concat(tslintRulesOutFiles).concat(["build-rules-end"]));
tslintRulesFiles.forEach(function(ruleFile, i) {
compileFile(tslintRulesOutFiles[i], [ruleFile], [ruleFile], [], /*useBuiltCompiler*/ false,
{ noOutFile: true, generateDeclarations: false, outDir: path.join(builtLocalDirectory, "tslint")});
});
-function getLinterOptions() {
- return {
- configuration: require("./tslint.json"),
- formatter: "prose",
- formattersDirectory: undefined,
- rulesDirectory: "built/local/tslint"
- };
-}
+desc("Emit the start of the build-rules fold");
+task("build-rules-start", [] , function() {
+ if (fold.isTravis()) console.log(fold.start("build-rules"));
+});
-function lintFileContents(options, path, contents) {
- var ll = new Linter(path, contents, options);
- console.log("Linting '" + path + "'.");
- return ll.lint();
-}
-
-function lintFile(options, path) {
- var contents = fs.readFileSync(path, "utf8");
- return lintFileContents(options, path, contents);
-}
-
-function lintFileAsync(options, path, cb) {
- fs.readFile(path, "utf8", function(err, contents) {
- if (err) {
- return cb(err);
- }
- var result = lintFileContents(options, path, contents);
- cb(undefined, result);
- });
-}
+desc("Emit the end of the build-rules fold");
+task("build-rules-end", [] , function() {
+ if (fold.isTravis()) console.log(fold.end("build-rules"));
+});
var lintTargets = compilerSources
.concat(harnessSources)
@@ -1041,73 +1061,81 @@ var lintTargets = compilerSources
.concat(serverCoreSources)
.concat(tslintRulesFiles)
.concat(servicesSources)
- .concat(["Gulpfile.ts"]);
+ .concat(["Gulpfile.ts"])
+ .concat([nodeServerInFile, perftscPath, "tests/perfsys.ts", webhostPath]);
+function sendNextFile(files, child, callback, failures) {
+ var file = files.pop();
+ if (file) {
+ console.log("Linting '" + file + "'.");
+ child.send({kind: "file", name: file});
+ }
+ else {
+ child.send({kind: "close"});
+ callback(failures);
+ }
+}
+
+function spawnLintWorker(files, callback) {
+ var child = child_process.fork("./scripts/parallel-lint");
+ var failures = 0;
+ child.on("message", function(data) {
+ switch (data.kind) {
+ case "result":
+ if (data.failures > 0) {
+ failures += data.failures;
+ console.log(data.output);
+ }
+ sendNextFile(files, child, callback, failures);
+ break;
+ case "error":
+ console.error(data.error);
+ failures++;
+ sendNextFile(files, child, callback, failures);
+ break;
+ }
+ });
+ sendNextFile(files, child, callback, failures);
+}
desc("Runs tslint on the compiler sources. Optional arguments are: f[iles]=regex");
task("lint", ["build-rules"], function() {
- var lintOptions = getLinterOptions();
+ if (fold.isTravis()) console.log(fold.start("lint"));
+ var startTime = mark();
var failed = 0;
var fileMatcher = RegExp(process.env.f || process.env.file || process.env.files || "");
var done = {};
for (var i in lintTargets) {
var target = lintTargets[i];
if (!done[target] && fileMatcher.test(target)) {
- var result = lintFile(lintOptions, target);
- if (result.failureCount > 0) {
- console.log(result.output);
- failed += result.failureCount;
- }
- done[target] = true;
+ done[target] = fs.statSync(target).size;
}
}
- if (failed > 0) {
- fail('Linter errors.', failed);
- }
-});
-/**
- * This is required because file watches on Windows get fires _twice_
- * when a file changes on some node/windows version configuations
- * (node v4 and win 10, for example). By not running a lint for a file
- * which already has a pending lint, we avoid duplicating our work.
- * (And avoid printing duplicate results!)
- */
-var lintSemaphores = {};
+ var workerCount = (process.env.workerCount && +process.env.workerCount) || os.cpus().length;
-function lintWatchFile(filename) {
- fs.watch(filename, {persistent: true}, function(event) {
- if (event !== "change") {
- return;
- }
-
- if (!lintSemaphores[filename]) {
- lintSemaphores[filename] = true;
- lintFileAsync(getLinterOptions(), filename, function(err, result) {
- delete lintSemaphores[filename];
- if (err) {
- console.log(err);
- return;
- }
- if (result.failureCount > 0) {
- console.log("***Lint failure***");
- for (var i = 0; i < result.failures.length; i++) {
- var failure = result.failures[i];
- var start = failure.startPosition.lineAndCharacter;
- var end = failure.endPosition.lineAndCharacter;
- console.log("warning " + filename + " (" + (start.line + 1) + "," + (start.character + 1) + "," + (end.line + 1) + "," + (end.character + 1) + "): " + failure.failure);
- }
- console.log("*** Total " + result.failureCount + " failures.");
- }
- });
- }
+ var names = Object.keys(done).sort(function(namea, nameb) {
+ return done[namea] - done[nameb];
});
-}
-desc("Watches files for changes to rerun a lint pass");
-task("lint-server", ["build-rules"], function() {
- console.log("Watching ./src for changes to linted files");
- for (var i = 0; i < lintTargets.length; i++) {
- lintWatchFile(lintTargets[i]);
+ for (var i = 0; i < workerCount; i++) {
+ spawnLintWorker(names, finished);
}
-});
+
+ var completed = 0;
+ var failures = 0;
+ function finished(fails) {
+ completed++;
+ failures += fails;
+ if (completed === workerCount) {
+ measure(startTime);
+ if (fold.isTravis()) console.log(fold.end("lint"));
+ if (failures > 0) {
+ fail('Linter errors.', failed);
+ }
+ else {
+ complete();
+ }
+ }
+ }
+}, {async: true});
diff --git a/README.md b/README.md
index fca2890bc77..d16bc363b26 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[](https://gitter.im/Microsoft/TypeScript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-[TypeScript](http://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types, classes, and modules to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](http://www.typescriptlang.org/Playground), and stay up to date via [our blog](http://blogs.msdn.com/typescript) and [Twitter account](https://twitter.com/typescriptlang).
+[TypeScript](http://www.typescriptlang.org/) is a language for application-scale JavaScript. TypeScript adds optional types, classes, and modules to JavaScript. TypeScript supports tools for large-scale JavaScript applications for any browser, for any host, on any OS. TypeScript compiles to readable, standards-based JavaScript. Try it out at the [playground](http://www.typescriptlang.org/Playground), and stay up to date via [our blog](https://blogs.msdn.microsoft.com/typescript) and [Twitter account](https://twitter.com/typescriptlang).
## Installing
diff --git a/package.json b/package.json
index 5606a423a0e..eb34b6bffbb 100644
--- a/package.json
+++ b/package.json
@@ -30,8 +30,8 @@
},
"devDependencies": {
"@types/browserify": "latest",
- "@types/convert-source-map": "latest",
"@types/chai": "latest",
+ "@types/convert-source-map": "latest",
"@types/del": "latest",
"@types/glob": "latest",
"@types/gulp": "latest",
@@ -69,16 +69,18 @@
"mkdirp": "latest",
"mocha": "latest",
"mocha-fivemat-progress-reporter": "latest",
+ "q": "latest",
"run-sequence": "latest",
"sorcery": "latest",
"through2": "latest",
+ "travis-fold": "latest",
"ts-node": "latest",
"tslint": "next",
"typescript": "next"
},
"scripts": {
"pretest": "jake tests",
- "test": "jake runtests",
+ "test": "jake runtests-parallel",
"build": "npm run build:compiler && npm run build:tests",
"build:compiler": "jake local",
"build:tests": "jake tests",
diff --git a/scripts/ior.ts b/scripts/ior.ts
index eb67e62a275..91580203350 100644
--- a/scripts/ior.ts
+++ b/scripts/ior.ts
@@ -1,4 +1,4 @@
-///
+///
import fs = require('fs');
import path = require('path');
diff --git a/scripts/parallel-lint.js b/scripts/parallel-lint.js
new file mode 100644
index 00000000000..a9aec06c2df
--- /dev/null
+++ b/scripts/parallel-lint.js
@@ -0,0 +1,45 @@
+var Linter = require("tslint");
+var fs = require("fs");
+
+function getLinterOptions() {
+ return {
+ configuration: require("../tslint.json"),
+ formatter: "prose",
+ formattersDirectory: undefined,
+ rulesDirectory: "built/local/tslint"
+ };
+}
+
+function lintFileContents(options, path, contents) {
+ var ll = new Linter(path, contents, options);
+ return ll.lint();
+}
+
+function lintFileAsync(options, path, cb) {
+ fs.readFile(path, "utf8", function (err, contents) {
+ if (err) {
+ return cb(err);
+ }
+ var result = lintFileContents(options, path, contents);
+ cb(undefined, result);
+ });
+}
+
+process.on("message", function (data) {
+ switch (data.kind) {
+ case "file":
+ var target = data.name;
+ var lintOptions = getLinterOptions();
+ lintFileAsync(lintOptions, target, function (err, result) {
+ if (err) {
+ process.send({ kind: "error", error: err.toString() });
+ return;
+ }
+ process.send({ kind: "result", failures: result.failureCount, output: result.output });
+ });
+ break;
+ case "close":
+ process.exit(0);
+ break;
+ }
+});
\ No newline at end of file
diff --git a/scripts/processDiagnosticMessages.ts b/scripts/processDiagnosticMessages.ts
index 26632ba6bab..431cf460180 100644
--- a/scripts/processDiagnosticMessages.ts
+++ b/scripts/processDiagnosticMessages.ts
@@ -69,7 +69,7 @@ function checkForUniqueCodes(messages: string[], diagnosticTable: InputDiagnosti
}
function buildUniqueNameMap(names: string[]): ts.Map {
- var nameMap: ts.Map = {};
+ var nameMap = ts.createMap();
var uniqueNames = NameGenerator.ensureUniqueness(names, /* isCaseSensitive */ false, /* isFixed */ undefined);
diff --git a/scripts/tslint/noTypeAssertionWhitespaceRule.ts b/scripts/tslint/noTypeAssertionWhitespaceRule.ts
new file mode 100644
index 00000000000..e75964b9e7e
--- /dev/null
+++ b/scripts/tslint/noTypeAssertionWhitespaceRule.ts
@@ -0,0 +1,25 @@
+import * as Lint from "tslint/lib/lint";
+import * as ts from "typescript";
+
+
+export class Rule extends Lint.Rules.AbstractRule {
+ public static TRAILING_FAILURE_STRING = "Excess trailing whitespace found around type assertion.";
+
+ public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
+ return this.applyWithWalker(new TypeAssertionWhitespaceWalker(sourceFile, this.getOptions()));
+ }
+}
+
+class TypeAssertionWhitespaceWalker extends Lint.RuleWalker {
+ public visitNode(node: ts.Node) {
+ if (node.kind === ts.SyntaxKind.TypeAssertionExpression) {
+ const refined = node as ts.TypeAssertion;
+ const leftSideWhitespaceStart = refined.type.getEnd() + 1;
+ const rightSideWhitespaceEnd = refined.expression.getStart();
+ if (leftSideWhitespaceStart !== rightSideWhitespaceEnd) {
+ this.addFailure(this.createFailure(leftSideWhitespaceStart, rightSideWhitespaceEnd, Rule.TRAILING_FAILURE_STRING));
+ }
+ }
+ super.visitNode(node);
+ }
+}
diff --git a/scripts/tslint/preferConstRule.ts b/scripts/tslint/preferConstRule.ts
index aaa1b0e53d5..1d316692468 100644
--- a/scripts/tslint/preferConstRule.ts
+++ b/scripts/tslint/preferConstRule.ts
@@ -1,7 +1,6 @@
import * as Lint from "tslint/lib/lint";
import * as ts from "typescript";
-
export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING_FACTORY = (identifier: string) => `Identifier '${identifier}' never appears on the LHS of an assignment - use const instead of let for its declaration.`;
@@ -64,7 +63,7 @@ interface DeclarationUsages {
}
class PreferConstWalker extends Lint.RuleWalker {
- private inScopeLetDeclarations: ts.Map[] = [];
+ private inScopeLetDeclarations: ts.MapLike[] = [];
private errors: Lint.RuleFailure[] = [];
private markAssignment(identifier: ts.Identifier) {
const name = identifier.text;
@@ -172,7 +171,7 @@ class PreferConstWalker extends Lint.RuleWalker {
}
private visitAnyForStatement(node: ts.ForOfStatement | ts.ForInStatement) {
- const names: ts.Map = {};
+ const names: ts.MapLike = {};
if (isLet(node.initializer)) {
if (node.initializer.kind === ts.SyntaxKind.VariableDeclarationList) {
this.collectLetIdentifiers(node.initializer as ts.VariableDeclarationList, names);
@@ -194,7 +193,7 @@ class PreferConstWalker extends Lint.RuleWalker {
}
visitBlock(node: ts.Block) {
- const names: ts.Map = {};
+ const names: ts.MapLike = {};
for (const statement of node.statements) {
if (statement.kind === ts.SyntaxKind.VariableStatement) {
this.collectLetIdentifiers((statement as ts.VariableStatement).declarationList, names);
@@ -205,7 +204,7 @@ class PreferConstWalker extends Lint.RuleWalker {
this.popDeclarations();
}
- private collectLetIdentifiers(list: ts.VariableDeclarationList, ret: ts.Map) {
+ private collectLetIdentifiers(list: ts.VariableDeclarationList, ret: ts.MapLike) {
for (const node of list.declarations) {
if (isLet(node) && !isExported(node)) {
this.collectNameIdentifiers(node, node.name, ret);
@@ -213,7 +212,7 @@ class PreferConstWalker extends Lint.RuleWalker {
}
}
- private collectNameIdentifiers(declaration: ts.VariableDeclaration, node: ts.Identifier | ts.BindingPattern, table: ts.Map) {
+ private collectNameIdentifiers(declaration: ts.VariableDeclaration, node: ts.Identifier | ts.BindingPattern, table: ts.MapLike) {
if (node.kind === ts.SyntaxKind.Identifier) {
table[(node as ts.Identifier).text] = { declaration, usages: 0 };
}
@@ -222,7 +221,7 @@ class PreferConstWalker extends Lint.RuleWalker {
}
}
- private collectBindingPatternIdentifiers(value: ts.VariableDeclaration, pattern: ts.BindingPattern, table: ts.Map) {
+ private collectBindingPatternIdentifiers(value: ts.VariableDeclaration, pattern: ts.BindingPattern, table: ts.MapLike) {
for (const element of pattern.elements) {
this.collectNameIdentifiers(value, element.name, table);
}
diff --git a/scripts/types/ambient.d.ts b/scripts/types/ambient.d.ts
index e77e3fe8c5a..4f4b118c432 100644
--- a/scripts/types/ambient.d.ts
+++ b/scripts/types/ambient.d.ts
@@ -10,7 +10,7 @@ declare module "gulp-insert" {
export function append(text: string | Buffer): NodeJS.ReadWriteStream;
export function prepend(text: string | Buffer): NodeJS.ReadWriteStream;
export function wrap(text: string | Buffer, tail: string | Buffer): NodeJS.ReadWriteStream;
- export function transform(cb: (contents: string, file: {path: string}) => string): NodeJS.ReadWriteStream; // file is a vinyl file
+ export function transform(cb: (contents: string, file: {path: string, relative: string}) => string): NodeJS.ReadWriteStream; // file is a vinyl file
}
declare module "into-stream" {
@@ -22,3 +22,4 @@ declare module "into-stream" {
}
declare module "sorcery";
+declare module "travis-fold";
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 6059cd1f86a..d8017d601ad 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -89,9 +89,10 @@ namespace ts {
const binder = createBinder();
export function bindSourceFile(file: SourceFile, options: CompilerOptions) {
- const start = performance.mark();
+ performance.mark("beforeBind");
binder(file, options);
- performance.measure("Bind", start);
+ performance.mark("afterBind");
+ performance.measure("Bind", "beforeBind", "afterBind");
}
function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
@@ -135,7 +136,7 @@ namespace ts {
options = opts;
languageVersion = getEmitScriptTarget(options);
inStrictMode = !!file.externalModuleIndicator;
- classifiableNames = {};
+ classifiableNames = createMap();
symbolCount = 0;
Symbol = objectAllocator.getSymbolConstructor();
@@ -183,11 +184,11 @@ namespace ts {
symbol.declarations.push(node);
if (symbolFlags & SymbolFlags.HasExports && !symbol.exports) {
- symbol.exports = {};
+ symbol.exports = createMap();
}
if (symbolFlags & SymbolFlags.HasMembers && !symbol.members) {
- symbol.members = {};
+ symbol.members = createMap();
}
if (symbolFlags & SymbolFlags.Value) {
@@ -298,8 +299,10 @@ namespace ts {
const name = isDefaultExport && parent ? "default" : getDeclarationName(node);
let symbol: Symbol;
- if (name !== undefined) {
-
+ if (name === undefined) {
+ symbol = createSymbol(SymbolFlags.None, "__missing");
+ }
+ else {
// Check and see if the symbol table already has a symbol with this name. If not,
// create a new symbol with this name and add it to the table. Note that we don't
// give the new symbol any flags *yet*. This ensures that it will not conflict
@@ -311,6 +314,11 @@ namespace ts {
// declaration we have for this symbol, and then create a new symbol for this
// declaration.
//
+ // Note that when properties declared in Javascript constructors
+ // (marked by isReplaceableByMethod) conflict with another symbol, the property loses.
+ // Always. This allows the common Javascript pattern of overwriting a prototype method
+ // with an bound instance method of the same type: `this.method = this.method.bind(this)`
+ //
// If we created a new symbol, either because we didn't have a symbol with this name
// in the symbol table, or we conflicted with an existing symbol, then just add this
// node as the sole declaration of the new symbol.
@@ -318,42 +326,44 @@ namespace ts {
// Otherwise, we'll be merging into a compatible existing symbol (for example when
// you have multiple 'vars' with the same name in the same container). In this case
// just add this node into the declarations list of the symbol.
- symbol = hasProperty(symbolTable, name)
- ? symbolTable[name]
- : (symbolTable[name] = createSymbol(SymbolFlags.None, name));
+ symbol = symbolTable[name] || (symbolTable[name] = createSymbol(SymbolFlags.None, name));
if (name && (includes & SymbolFlags.Classifiable)) {
classifiableNames[name] = name;
}
if (symbol.flags & excludes) {
- if (node.name) {
- node.name.parent = node;
+ if (symbol.isReplaceableByMethod) {
+ // Javascript constructor-declared symbols can be discarded in favor of
+ // prototype symbols like methods.
+ symbol = symbolTable[name] = createSymbol(SymbolFlags.None, name);
}
-
- // Report errors every position with duplicate declaration
- // Report errors on previous encountered declarations
- let message = symbol.flags & SymbolFlags.BlockScopedVariable
- ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
- : Diagnostics.Duplicate_identifier_0;
-
- forEach(symbol.declarations, declaration => {
- if (declaration.flags & NodeFlags.Default) {
- message = Diagnostics.A_module_cannot_have_multiple_default_exports;
+ else {
+ if (node.name) {
+ node.name.parent = node;
}
- });
- forEach(symbol.declarations, declaration => {
- file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration)));
- });
- file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node)));
+ // Report errors every position with duplicate declaration
+ // Report errors on previous encountered declarations
+ let message = symbol.flags & SymbolFlags.BlockScopedVariable
+ ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
+ : Diagnostics.Duplicate_identifier_0;
- symbol = createSymbol(SymbolFlags.None, name);
+ forEach(symbol.declarations, declaration => {
+ if (declaration.flags & NodeFlags.Default) {
+ message = Diagnostics.A_module_cannot_have_multiple_default_exports;
+ }
+ });
+
+ forEach(symbol.declarations, declaration => {
+ file.bindDiagnostics.push(createDiagnosticForNode(declaration.name || declaration, message, getDisplayName(declaration)));
+ });
+ file.bindDiagnostics.push(createDiagnosticForNode(node.name || node, message, getDisplayName(node)));
+
+ symbol = createSymbol(SymbolFlags.None, name);
+ }
}
}
- else {
- symbol = createSymbol(SymbolFlags.None, "__missing");
- }
addDeclarationToSymbol(symbol, node, includes);
symbol.parent = parent;
@@ -434,7 +444,7 @@ namespace ts {
if (containerFlags & ContainerFlags.IsContainer) {
container = blockScopeContainer = node;
if (containerFlags & ContainerFlags.HasLocals) {
- container.locals = {};
+ container.locals = createMap();
}
addToContainerChain(container);
}
@@ -618,18 +628,10 @@ namespace ts {
return false;
}
- function isNarrowingNullCheckOperands(expr1: Expression, expr2: Expression) {
- return (expr1.kind === SyntaxKind.NullKeyword || expr1.kind === SyntaxKind.Identifier && (expr1).text === "undefined") && isNarrowableOperand(expr2);
- }
-
function isNarrowingTypeofOperands(expr1: Expression, expr2: Expression) {
return expr1.kind === SyntaxKind.TypeOfExpression && isNarrowableOperand((expr1).expression) && expr2.kind === SyntaxKind.StringLiteral;
}
- function isNarrowingDiscriminant(expr: Expression) {
- return expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression);
- }
-
function isNarrowingBinaryExpression(expr: BinaryExpression) {
switch (expr.operatorToken.kind) {
case SyntaxKind.EqualsToken:
@@ -638,9 +640,8 @@ namespace ts {
case SyntaxKind.ExclamationEqualsToken:
case SyntaxKind.EqualsEqualsEqualsToken:
case SyntaxKind.ExclamationEqualsEqualsToken:
- return isNarrowingNullCheckOperands(expr.right, expr.left) || isNarrowingNullCheckOperands(expr.left, expr.right) ||
- isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right) ||
- isNarrowingDiscriminant(expr.left) || isNarrowingDiscriminant(expr.right);
+ return isNarrowableOperand(expr.left) || isNarrowableOperand(expr.right) ||
+ isNarrowingTypeofOperands(expr.right, expr.left) || isNarrowingTypeofOperands(expr.left, expr.right);
case SyntaxKind.InstanceOfKeyword:
return isNarrowableOperand(expr.left);
case SyntaxKind.CommaToken:
@@ -664,11 +665,6 @@ namespace ts {
return isNarrowableReference(expr);
}
- function isNarrowingSwitchStatement(switchStatement: SwitchStatement) {
- const expr = switchStatement.expression;
- return expr.kind === SyntaxKind.PropertyAccessExpression && isNarrowableReference((expr).expression);
- }
-
function createBranchLabel(): FlowLabel {
return {
flags: FlowFlags.BranchLabel,
@@ -718,7 +714,7 @@ namespace ts {
}
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
- if (!isNarrowingSwitchStatement(switchStatement)) {
+ if (!isNarrowingExpression(switchStatement.expression)) {
return antecedent;
}
setFlowNodeReferenced(antecedent);
@@ -1413,7 +1409,8 @@ namespace ts {
const typeLiteralSymbol = createSymbol(SymbolFlags.TypeLiteral, "__type");
addDeclarationToSymbol(typeLiteralSymbol, node, SymbolFlags.TypeLiteral);
- typeLiteralSymbol.members = { [symbol.name]: symbol };
+ typeLiteralSymbol.members = createMap();
+ typeLiteralSymbol.members[symbol.name] = symbol;
}
function bindObjectLiteralExpression(node: ObjectLiteralExpression) {
@@ -1423,7 +1420,7 @@ namespace ts {
}
if (inStrictMode) {
- const seen: Map = {};
+ const seen = createMap();
for (const prop of node.properties) {
if (prop.name.kind !== SyntaxKind.Identifier) {
@@ -1479,7 +1476,7 @@ namespace ts {
// fall through.
default:
if (!blockScopeContainer.locals) {
- blockScopeContainer.locals = {};
+ blockScopeContainer.locals = createMap();
addToContainerChain(blockScopeContainer);
}
declareSymbol(blockScopeContainer.locals, undefined, node, symbolFlags, symbolExcludes);
@@ -1901,18 +1898,17 @@ namespace ts {
}
function bindExportAssignment(node: ExportAssignment | BinaryExpression) {
- const boundExpression = node.kind === SyntaxKind.ExportAssignment ? (node).expression : (node).right;
if (!container.symbol || !container.symbol.exports) {
// Export assignment in some sort of block construct
bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node));
}
- else if (boundExpression.kind === SyntaxKind.Identifier && node.kind === SyntaxKind.ExportAssignment) {
- // An export default clause with an identifier exports all meanings of that identifier
- declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Alias, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes);
- }
else {
- // An export default clause with an expression exports a value
- declareSymbol(container.symbol.exports, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes);
+ const flags = node.kind === SyntaxKind.ExportAssignment && exportAssignmentIsAlias(node)
+ // An export default clause with an EntityNameExpression exports all meanings of that identifier
+ ? SymbolFlags.Alias
+ // An export default clause with any other expression exports a value
+ : SymbolFlags.Property;
+ declareSymbol(container.symbol.exports, container.symbol, node, flags, SymbolFlags.PropertyExcludes | SymbolFlags.AliasExcludes);
}
}
@@ -1939,7 +1935,7 @@ namespace ts {
}
}
- file.symbol.globalExports = file.symbol.globalExports || {};
+ file.symbol.globalExports = file.symbol.globalExports || createMap();
declareSymbol(file.symbol.globalExports, file.symbol, node, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
@@ -1981,20 +1977,25 @@ namespace ts {
}
function bindThisPropertyAssignment(node: BinaryExpression) {
- // Declare a 'member' in case it turns out the container was an ES5 class or ES6 constructor
- let assignee: Node;
- if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionDeclaration) {
- assignee = container;
+ Debug.assert(isInJavaScriptFile(node));
+ // Declare a 'member' if the container is an ES5 class or ES6 constructor
+ if (container.kind === SyntaxKind.FunctionDeclaration || container.kind === SyntaxKind.FunctionExpression) {
+ container.symbol.members = container.symbol.members || createMap();
+ // It's acceptable for multiple 'this' assignments of the same identifier to occur
+ declareSymbol(container.symbol.members, container.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
}
else if (container.kind === SyntaxKind.Constructor) {
- assignee = container.parent;
+ // this.foo assignment in a JavaScript class
+ // Bind this property to the containing class
+ const saveContainer = container;
+ container = container.parent;
+ const symbol = bindPropertyOrMethodOrAccessor(node, SymbolFlags.Property, SymbolFlags.None);
+ if (symbol) {
+ // constructor-declared symbols can be overwritten by subsequent method declarations
+ (symbol as Symbol).isReplaceableByMethod = true;
+ }
+ container = saveContainer;
}
- else {
- return;
- }
- assignee.symbol.members = assignee.symbol.members || {};
- // It's acceptable for multiple 'this' assignments of the same identifier to occur
- declareSymbol(assignee.symbol.members, assignee.symbol, node, SymbolFlags.Property, SymbolFlags.PropertyExcludes & ~SymbolFlags.Property);
}
function bindPrototypePropertyAssignment(node: BinaryExpression) {
@@ -2018,7 +2019,7 @@ namespace ts {
// Set up the members collection if it doesn't exist already
if (!funcSymbol.members) {
- funcSymbol.members = {};
+ funcSymbol.members = createMap();
}
// Declare the method/property
@@ -2067,7 +2068,7 @@ namespace ts {
// module might have an exported variable called 'prototype'. We can't allow that as
// that would clash with the built-in 'prototype' for the class.
const prototypeSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Prototype, "prototype");
- if (hasProperty(symbol.exports, prototypeSymbol.name)) {
+ if (symbol.exports[prototypeSymbol.name]) {
if (node.name) {
node.name.parent = node;
}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 1cebd469388..f7d0fd36f43 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -44,7 +44,7 @@ namespace ts {
let symbolCount = 0;
const emptyArray: any[] = [];
- const emptySymbols: SymbolTable = {};
+ const emptySymbols = createMap();
const compilerOptions = host.getCompilerOptions();
const languageVersion = compilerOptions.target || ScriptTarget.ES3;
@@ -106,25 +106,33 @@ namespace ts {
isOptionalParameter
};
+ const tupleTypes = createMap();
+ const unionTypes = createMap();
+ const intersectionTypes = createMap();
+ const stringLiteralTypes = createMap();
+ const numericLiteralTypes = createMap();
+
const unknownSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "unknown");
const resolvingSymbol = createSymbol(SymbolFlags.Transient, "__resolving__");
const anyType = createIntrinsicType(TypeFlags.Any, "any");
- const stringType = createIntrinsicType(TypeFlags.String, "string");
- const numberType = createIntrinsicType(TypeFlags.Number, "number");
- const booleanType = createIntrinsicType(TypeFlags.Boolean, "boolean");
- const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
- const voidType = createIntrinsicType(TypeFlags.Void, "void");
+ const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
const undefinedType = createIntrinsicType(TypeFlags.Undefined, "undefined");
const undefinedWideningType = strictNullChecks ? undefinedType : createIntrinsicType(TypeFlags.Undefined | TypeFlags.ContainsWideningType, "undefined");
const nullType = createIntrinsicType(TypeFlags.Null, "null");
const nullWideningType = strictNullChecks ? nullType : createIntrinsicType(TypeFlags.Null | TypeFlags.ContainsWideningType, "null");
- const unknownType = createIntrinsicType(TypeFlags.Any, "unknown");
+ const stringType = createIntrinsicType(TypeFlags.String, "string");
+ const numberType = createIntrinsicType(TypeFlags.Number, "number");
+ const trueType = createIntrinsicType(TypeFlags.BooleanLiteral, "true");
+ const falseType = createIntrinsicType(TypeFlags.BooleanLiteral, "false");
+ const booleanType = createBooleanType([trueType, falseType]);
+ const esSymbolType = createIntrinsicType(TypeFlags.ESSymbol, "symbol");
+ const voidType = createIntrinsicType(TypeFlags.Void, "void");
const neverType = createIntrinsicType(TypeFlags.Never, "never");
const emptyObjectType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
const emptyGenericType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
- emptyGenericType.instantiations = {};
+ emptyGenericType.instantiations = createMap();
const anyFunctionType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
// The anyFunctionType contains the anyFunctionType by definition. The flag is further propagated
@@ -133,12 +141,12 @@ namespace ts {
const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined);
- const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false);
- const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false);
+ const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
+ const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true);
- const globals: SymbolTable = {};
+ const globals = createMap();
/**
* List of every ambient module with a "*" wildcard.
* Unlike other ambient modules, these can't be stored in `globals` because symbol tables only deal with exact matches.
@@ -192,10 +200,8 @@ namespace ts {
let flowLoopCount = 0;
let visitedFlowCount = 0;
- const tupleTypes: Map = {};
- const unionTypes: Map = {};
- const intersectionTypes: Map = {};
- const stringLiteralTypes: Map = {};
+ const emptyStringType = getLiteralTypeForText(TypeFlags.StringLiteral, "");
+ const zeroType = getLiteralTypeForText(TypeFlags.NumberLiteral, "0");
const resolutionTargets: TypeSystemEntity[] = [];
const resolutionResults: boolean[] = [];
@@ -209,7 +215,7 @@ namespace ts {
const flowLoopKeys: string[] = [];
const flowLoopTypes: Type[][] = [];
const visitedFlowNodes: FlowNode[] = [];
- const visitedFlowTypes: Type[] = [];
+ const visitedFlowTypes: FlowType[] = [];
const potentialThisCollisions: Node[] = [];
const awaitedTypeStack: number[] = [];
@@ -239,27 +245,46 @@ namespace ts {
NEUndefinedOrNull = 1 << 19, // x != undefined / x != null
Truthy = 1 << 20, // x
Falsy = 1 << 21, // !x
- All = (1 << 22) - 1,
+ Discriminatable = 1 << 22, // May have discriminant property
+ All = (1 << 23) - 1,
// The following members encode facts about particular kinds of types for use in the getTypeFacts function.
// The presence of a particular fact means that the given test is true for some (and possibly all) values
// of that kind of type.
- StringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy,
- StringFacts = StringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull,
- NumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy,
- NumberFacts = NumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull,
- BooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Falsy,
- BooleanFacts = BooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull,
+ BaseStringStrictFacts = TypeofEQString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
+ BaseStringFacts = BaseStringStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ StringStrictFacts = BaseStringStrictFacts | Truthy | Falsy,
+ StringFacts = BaseStringFacts | Truthy,
+ EmptyStringStrictFacts = BaseStringStrictFacts | Falsy,
+ EmptyStringFacts = BaseStringFacts,
+ NonEmptyStringStrictFacts = BaseStringStrictFacts | Truthy,
+ NonEmptyStringFacts = BaseStringFacts | Truthy,
+ BaseNumberStrictFacts = TypeofEQNumber | TypeofNEString | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
+ BaseNumberFacts = BaseNumberStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ NumberStrictFacts = BaseNumberStrictFacts | Truthy | Falsy,
+ NumberFacts = BaseNumberFacts | Truthy,
+ ZeroStrictFacts = BaseNumberStrictFacts | Falsy,
+ ZeroFacts = BaseNumberFacts,
+ NonZeroStrictFacts = BaseNumberStrictFacts | Truthy,
+ NonZeroFacts = BaseNumberFacts | Truthy,
+ BaseBooleanStrictFacts = TypeofEQBoolean | TypeofNEString | TypeofNENumber | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull,
+ BaseBooleanFacts = BaseBooleanStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
+ BooleanStrictFacts = BaseBooleanStrictFacts | Truthy | Falsy,
+ BooleanFacts = BaseBooleanFacts | Truthy,
+ FalseStrictFacts = BaseBooleanStrictFacts | Falsy,
+ FalseFacts = BaseBooleanFacts,
+ TrueStrictFacts = BaseBooleanStrictFacts | Truthy,
+ TrueFacts = BaseBooleanFacts | Truthy,
SymbolStrictFacts = TypeofEQSymbol | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
SymbolFacts = SymbolStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
+ ObjectStrictFacts = TypeofEQObject | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable,
ObjectFacts = ObjectStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
- FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy,
+ FunctionStrictFacts = TypeofEQFunction | TypeofEQHostObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | NEUndefined | NENull | NEUndefinedOrNull | Truthy | Discriminatable,
FunctionFacts = FunctionStrictFacts | EQUndefined | EQNull | EQUndefinedOrNull | Falsy,
UndefinedFacts = TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEObject | TypeofNEFunction | TypeofNEHostObject | EQUndefined | EQUndefinedOrNull | NENull | Falsy,
NullFacts = TypeofEQObject | TypeofNEString | TypeofNENumber | TypeofNEBoolean | TypeofNESymbol | TypeofNEFunction | TypeofNEHostObject | EQNull | EQUndefinedOrNull | NEUndefined | Falsy,
}
- const typeofEQFacts: Map = {
+ const typeofEQFacts = createMap({
"string": TypeFacts.TypeofEQString,
"number": TypeFacts.TypeofEQNumber,
"boolean": TypeFacts.TypeofEQBoolean,
@@ -267,9 +292,9 @@ namespace ts {
"undefined": TypeFacts.EQUndefined,
"object": TypeFacts.TypeofEQObject,
"function": TypeFacts.TypeofEQFunction
- };
+ });
- const typeofNEFacts: Map = {
+ const typeofNEFacts = createMap({
"string": TypeFacts.TypeofNEString,
"number": TypeFacts.TypeofNENumber,
"boolean": TypeFacts.TypeofNEBoolean,
@@ -277,19 +302,19 @@ namespace ts {
"undefined": TypeFacts.NEUndefined,
"object": TypeFacts.TypeofNEObject,
"function": TypeFacts.TypeofNEFunction
- };
+ });
- const typeofTypesByName: Map = {
+ const typeofTypesByName = createMap({
"string": stringType,
"number": numberType,
"boolean": booleanType,
"symbol": esSymbolType,
"undefined": undefinedType
- };
+ });
let jsxElementType: ObjectType;
/** Things we lazy load from the JSX namespace */
- const jsxTypes: Map = {};
+ const jsxTypes = createMap();
const JsxNames = {
JSX: "JSX",
IntrinsicElements: "IntrinsicElements",
@@ -300,10 +325,10 @@ namespace ts {
IntrinsicClassAttributes: "IntrinsicClassAttributes"
};
- const subtypeRelation: Map = {};
- const assignableRelation: Map = {};
- const comparableRelation: Map = {};
- const identityRelation: Map = {};
+ const subtypeRelation = createMap();
+ const assignableRelation = createMap();
+ const comparableRelation = createMap();
+ const identityRelation = createMap();
// This is for caching the result of getSymbolDisplayBuilder. Do not access directly.
let _displayBuilder: SymbolDisplayBuilder;
@@ -317,9 +342,8 @@ namespace ts {
ResolvedReturnType
}
- const builtinGlobals: SymbolTable = {
- [undefinedSymbol.name]: undefinedSymbol
- };
+ const builtinGlobals = createMap();
+ builtinGlobals[undefinedSymbol.name] = undefinedSymbol;
initializeTypeChecker();
@@ -379,8 +403,8 @@ namespace ts {
result.parent = symbol.parent;
if (symbol.valueDeclaration) result.valueDeclaration = symbol.valueDeclaration;
if (symbol.constEnumOnlyModule) result.constEnumOnlyModule = true;
- if (symbol.members) result.members = cloneSymbolTable(symbol.members);
- if (symbol.exports) result.exports = cloneSymbolTable(symbol.exports);
+ if (symbol.members) result.members = cloneMap(symbol.members);
+ if (symbol.exports) result.exports = cloneMap(symbol.exports);
recordMergedSymbol(result, symbol);
return result;
}
@@ -402,11 +426,11 @@ namespace ts {
target.declarations.push(node);
});
if (source.members) {
- if (!target.members) target.members = {};
+ if (!target.members) target.members = createMap();
mergeSymbolTable(target.members, source.members);
}
if (source.exports) {
- if (!target.exports) target.exports = {};
+ if (!target.exports) target.exports = createMap();
mergeSymbolTable(target.exports, source.exports);
}
recordMergedSymbol(target, source);
@@ -423,29 +447,17 @@ namespace ts {
}
}
- function cloneSymbolTable(symbolTable: SymbolTable): SymbolTable {
- const result: SymbolTable = {};
- for (const id in symbolTable) {
- if (hasProperty(symbolTable, id)) {
- result[id] = symbolTable[id];
- }
- }
- return result;
- }
-
function mergeSymbolTable(target: SymbolTable, source: SymbolTable) {
for (const id in source) {
- if (hasProperty(source, id)) {
- if (!hasProperty(target, id)) {
- target[id] = source[id];
- }
- else {
- let symbol = target[id];
- if (!(symbol.flags & SymbolFlags.Merged)) {
- target[id] = symbol = cloneSymbol(symbol);
- }
- mergeSymbol(symbol, source[id]);
+ let targetSymbol = target[id];
+ if (!targetSymbol) {
+ target[id] = source[id];
+ }
+ else {
+ if (!(targetSymbol.flags & SymbolFlags.Merged)) {
+ target[id] = targetSymbol = cloneSymbol(targetSymbol);
}
+ mergeSymbol(targetSymbol, source[id]);
}
}
}
@@ -489,14 +501,12 @@ namespace ts {
function addToSymbolTable(target: SymbolTable, source: SymbolTable, message: DiagnosticMessage) {
for (const id in source) {
- if (hasProperty(source, id)) {
- if (hasProperty(target, id)) {
- // Error on redeclarations
- forEach(target[id].declarations, addDeclarationDiagnostic(id, message));
- }
- else {
- target[id] = source[id];
- }
+ if (target[id]) {
+ // Error on redeclarations
+ forEach(target[id].declarations, addDeclarationDiagnostic(id, message));
+ }
+ else {
+ target[id] = source[id];
}
}
@@ -513,7 +523,7 @@ namespace ts {
function getNodeLinks(node: Node): NodeLinks {
const nodeId = getNodeId(node);
- return nodeLinks[nodeId] || (nodeLinks[nodeId] = {});
+ return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 });
}
function isGlobalSourceFile(node: Node) {
@@ -521,18 +531,20 @@ namespace ts {
}
function getSymbol(symbols: SymbolTable, name: string, meaning: SymbolFlags): Symbol {
- if (meaning && hasProperty(symbols, name)) {
+ if (meaning) {
const symbol = symbols[name];
- Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
- if (symbol.flags & meaning) {
- return symbol;
- }
- if (symbol.flags & SymbolFlags.Alias) {
- const target = resolveAlias(symbol);
- // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors
- if (target === unknownSymbol || target.flags & meaning) {
+ if (symbol) {
+ Debug.assert((symbol.flags & SymbolFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
+ if (symbol.flags & meaning) {
return symbol;
}
+ if (symbol.flags & SymbolFlags.Alias) {
+ const target = resolveAlias(symbol);
+ // Unknown symbol means an error occurred in alias resolution, treat it as positive answer to avoid cascading errors
+ if (target === unknownSymbol || target.flags & meaning) {
+ return symbol;
+ }
+ }
}
}
// return undefined if we can't find a symbol.
@@ -640,7 +652,7 @@ namespace ts {
// Resolve a given name for a given meaning at a given location. An error is reported if the name was not found and
// the nameNotFoundMessage argument is not undefined. Returns the resolved symbol, or undefined if no symbol with
// the given name can be found.
- function resolveName(location: Node, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol {
+ function resolveName(location: Node | undefined, name: string, meaning: SymbolFlags, nameNotFoundMessage: DiagnosticMessage, nameArg: string | Identifier): Symbol {
let result: Symbol;
let lastLocation: Node;
let propertyWithInvalidInitializer: Node;
@@ -720,7 +732,7 @@ namespace ts {
// 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely*
// an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
// which is not the desired behavior.
- if (hasProperty(moduleExports, name) &&
+ if (moduleExports[name] &&
moduleExports[name].flags === SymbolFlags.Alias &&
getDeclarationOfKind(moduleExports[name], SyntaxKind.ExportSpecifier)) {
break;
@@ -857,7 +869,8 @@ namespace ts {
if (!result) {
if (nameNotFoundMessage) {
- if (!checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) &&
+ if (!errorLocation ||
+ !checkAndReportErrorForMissingPrefix(errorLocation, name, nameArg) &&
!checkAndReportErrorForExtendingInterface(errorLocation)) {
error(errorLocation, nameNotFoundMessage, typeof nameArg === "string" ? nameArg : declarationNameToString(nameArg));
}
@@ -906,7 +919,7 @@ namespace ts {
}
function checkAndReportErrorForMissingPrefix(errorLocation: Node, name: string, nameArg: string | Identifier): boolean {
- if (!errorLocation || (errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(errorLocation)) || isInTypeQuery(errorLocation))) {
+ if ((errorLocation.kind === SyntaxKind.Identifier && (isTypeReferenceIdentifier(errorLocation)) || isInTypeQuery(errorLocation))) {
return false;
}
@@ -944,28 +957,30 @@ namespace ts {
function checkAndReportErrorForExtendingInterface(errorLocation: Node): boolean {
- let parentClassExpression = errorLocation;
- while (parentClassExpression) {
- const kind = parentClassExpression.kind;
- if (kind === SyntaxKind.Identifier || kind === SyntaxKind.PropertyAccessExpression) {
- parentClassExpression = parentClassExpression.parent;
- continue;
- }
- if (kind === SyntaxKind.ExpressionWithTypeArguments) {
- break;
- }
- return false;
- }
- if (!parentClassExpression) {
- return false;
- }
- const expression = (parentClassExpression).expression;
- if (resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true)) {
+ const expression = getEntityNameForExtendingInterface(errorLocation);
+ const isError = !!(expression && resolveEntityName(expression, SymbolFlags.Interface, /*ignoreErrors*/ true));
+ if (isError) {
error(errorLocation, Diagnostics.Cannot_extend_an_interface_0_Did_you_mean_implements, getTextOfNode(expression));
- return true;
}
- return false;
+ return isError;
}
+ /**
+ * Climbs up parents to an ExpressionWithTypeArguments, and returns its expression,
+ * but returns undefined if that expression is not an EntityNameExpression.
+ */
+ function getEntityNameForExtendingInterface(node: Node): EntityNameExpression | undefined {
+ switch (node.kind) {
+ case SyntaxKind.Identifier:
+ case SyntaxKind.PropertyAccessExpression:
+ return node.parent ? getEntityNameForExtendingInterface(node.parent) : undefined;
+ case SyntaxKind.ExpressionWithTypeArguments:
+ Debug.assert(isEntityNameExpression((node).expression));
+ return (node).expression;
+ default:
+ return undefined;
+ }
+ }
+
function checkResolvedBlockScopedVariable(result: Symbol, errorLocation: Node): void {
Debug.assert((result.flags & SymbolFlags.BlockScopedVariable) !== 0);
@@ -1009,7 +1024,7 @@ namespace ts {
}
function getDeclarationOfAliasSymbol(symbol: Symbol): Declaration {
- return forEach(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined);
+ return find(symbol.declarations, d => isAliasSymbolDeclaration(d) ? d : undefined);
}
function getTargetOfImportEqualsDeclaration(node: ImportEqualsDeclaration): Symbol {
@@ -1077,9 +1092,9 @@ namespace ts {
function getExportOfModule(symbol: Symbol, name: string): Symbol {
if (symbol.flags & SymbolFlags.Module) {
- const exports = getExportsOfSymbol(symbol);
- if (hasProperty(exports, name)) {
- return resolveSymbol(exports[name]);
+ const exportedSymbol = getExportsOfSymbol(symbol)[name];
+ if (exportedSymbol) {
+ return resolveSymbol(exportedSymbol);
}
}
}
@@ -1111,6 +1126,10 @@ namespace ts {
else {
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text);
}
+ // If the export member we're looking for is default, and there is no real default but allowSyntheticDefaultImports is on, return the entire module as the default
+ if (!symbolFromVariable && allowSyntheticDefaultImports && name.text === "default") {
+ symbolFromVariable = resolveExternalModuleSymbol(moduleSymbol) || resolveSymbol(moduleSymbol);
+ }
// if symbolFromVariable is export - get its final target
symbolFromVariable = resolveSymbol(symbolFromVariable);
const symbolFromModule = getExportOfModule(targetSymbol, name.text);
@@ -1140,7 +1159,7 @@ namespace ts {
}
function getTargetOfExportAssignment(node: ExportAssignment): Symbol {
- return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
+ return resolveEntityName(node.expression, SymbolFlags.Value | SymbolFlags.Type | SymbolFlags.Namespace);
}
function getTargetOfAliasDeclaration(node: Declaration): Symbol {
@@ -1250,7 +1269,7 @@ namespace ts {
}
// Resolves a qualified name and any involved aliases
- function resolveEntityName(name: EntityName | Expression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol {
+ function resolveEntityName(name: EntityNameOrEntityNameExpression, meaning: SymbolFlags, ignoreErrors?: boolean, dontResolveAlias?: boolean): Symbol | undefined {
if (nodeIsMissing(name)) {
return undefined;
}
@@ -1265,7 +1284,7 @@ namespace ts {
}
}
else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) {
- const left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression;
+ const left = name.kind === SyntaxKind.QualifiedName ? (name).left : (name).expression;
const right = name.kind === SyntaxKind.QualifiedName ? (name).right : (name).name;
const namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors);
@@ -1393,7 +1412,7 @@ namespace ts {
*/
function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: Map, exportNode?: ExportDeclaration) {
for (const id in source) {
- if (id !== "default" && !hasProperty(target, id)) {
+ if (id !== "default" && !target[id]) {
target[id] = source[id];
if (lookupTable && exportNode) {
lookupTable[id] = {
@@ -1401,7 +1420,7 @@ namespace ts {
} as ExportCollisionTracker;
}
}
- else if (lookupTable && exportNode && id !== "default" && hasProperty(target, id) && resolveSymbol(target[id]) !== resolveSymbol(source[id])) {
+ else if (lookupTable && exportNode && id !== "default" && target[id] && resolveSymbol(target[id]) !== resolveSymbol(source[id])) {
if (!lookupTable[id].exportsWithDuplicate) {
lookupTable[id].exportsWithDuplicate = [exportNode];
}
@@ -1423,12 +1442,12 @@ namespace ts {
return;
}
visitedSymbols.push(symbol);
- const symbols = cloneSymbolTable(symbol.exports);
+ const symbols = cloneMap(symbol.exports);
// All export * declarations are collected in an __export symbol by the binder
const exportStars = symbol.exports["__export"];
if (exportStars) {
- const nestedSymbols: SymbolTable = {};
- const lookupTable: Map = {};
+ const nestedSymbols = createMap();
+ const lookupTable = createMap();
for (const node of exportStars.declarations) {
const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier);
const exportedSymbols = visit(resolvedModule);
@@ -1442,7 +1461,7 @@ namespace ts {
for (const id in lookupTable) {
const { exportsWithDuplicate } = lookupTable[id];
// It's not an error if the file with multiple `export *`s with duplicate names exports a member with that name itself
- if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || hasProperty(symbols, id)) {
+ if (id === "export=" || !(exportsWithDuplicate && exportsWithDuplicate.length) || symbols[id]) {
continue;
}
for (const node of exportsWithDuplicate) {
@@ -1510,8 +1529,8 @@ namespace ts {
function createType(flags: TypeFlags): Type {
const result = new Type(checker, flags);
- result.id = typeCount;
typeCount++;
+ result.id = typeCount;
return result;
}
@@ -1521,6 +1540,13 @@ namespace ts {
return type;
}
+ function createBooleanType(trueFalseTypes: Type[]): IntrinsicType & UnionType {
+ const type = getUnionType(trueFalseTypes);
+ type.flags |= TypeFlags.Boolean;
+ type.intrinsicName = "boolean";
+ return type;
+ }
+
function createObjectType(kind: TypeFlags, symbol?: Symbol): ObjectType {
const type = createType(kind);
type.symbol = symbol;
@@ -1541,13 +1567,11 @@ namespace ts {
function getNamedMembers(members: SymbolTable): Symbol[] {
let result: Symbol[];
for (const id in members) {
- if (hasProperty(members, id)) {
- if (!isReservedMemberName(id)) {
- if (!result) result = [];
- const symbol = members[id];
- if (symbolIsValue(symbol)) {
- result.push(symbol);
- }
+ if (!isReservedMemberName(id)) {
+ if (!result) result = [];
+ const symbol = members[id];
+ if (symbolIsValue(symbol)) {
+ result.push(symbol);
}
}
}
@@ -1623,12 +1647,12 @@ namespace ts {
}
// If symbol is directly available by its name in the symbol table
- if (isAccessible(lookUp(symbols, symbol.name))) {
+ if (isAccessible(symbols[symbol.name])) {
return [symbol];
}
// Check if symbol is any of the alias
- return forEachValue(symbols, symbolFromSymbolTable => {
+ return forEachProperty(symbols, symbolFromSymbolTable => {
if (symbolFromSymbolTable.flags & SymbolFlags.Alias
&& symbolFromSymbolTable.name !== "export="
&& !getDeclarationOfKind(symbolFromSymbolTable, SyntaxKind.ExportSpecifier)) {
@@ -1663,12 +1687,12 @@ namespace ts {
let qualify = false;
forEachSymbolTableInScope(enclosingDeclaration, symbolTable => {
// If symbol of this name is not available in the symbol table we are ok
- if (!hasProperty(symbolTable, symbol.name)) {
+ let symbolFromSymbolTable = symbolTable[symbol.name];
+ if (!symbolFromSymbolTable) {
// Continue to the next symbol table
return false;
}
// If the symbol with this name is present it should refer to the symbol
- let symbolFromSymbolTable = symbolTable[symbol.name];
if (symbolFromSymbolTable === symbol) {
// No need to qualify
return true;
@@ -1814,7 +1838,7 @@ namespace ts {
}
}
- function isEntityNameVisible(entityName: EntityName | Expression, enclosingDeclaration: Node): SymbolVisibilityResult {
+ function isEntityNameVisible(entityName: EntityNameOrEntityNameExpression, enclosingDeclaration: Node): SymbolVisibilityResult {
// get symbol of the first identifier of the entityName
let meaning: SymbolFlags;
if (entityName.parent.kind === SyntaxKind.TypeQuery || isExpressionWithTypeArgumentsInClassExtendsClause(entityName.parent)) {
@@ -1895,6 +1919,30 @@ namespace ts {
return result;
}
+ function formatUnionTypes(types: Type[]): Type[] {
+ const result: Type[] = [];
+ let flags: TypeFlags = 0;
+ for (let i = 0; i < types.length; i++) {
+ const t = types[i];
+ flags |= t.flags;
+ if (!(t.flags & TypeFlags.Nullable)) {
+ if (t.flags & (TypeFlags.BooleanLiteral | TypeFlags.EnumLiteral)) {
+ const baseType = t.flags & TypeFlags.BooleanLiteral ? booleanType : (t).baseType;
+ const count = baseType.types.length;
+ if (i + count <= types.length && types[i + count - 1] === baseType.types[count - 1]) {
+ result.push(baseType);
+ i += count - 1;
+ continue;
+ }
+ }
+ result.push(t);
+ }
+ }
+ if (flags & TypeFlags.Null) result.push(nullType);
+ if (flags & TypeFlags.Undefined) result.push(undefinedType);
+ return result || types;
+ }
+
function visibilityToString(flags: NodeFlags) {
if (flags === NodeFlags.Private) {
return "private";
@@ -2064,6 +2112,7 @@ namespace ts {
return writeType(type, globalFlags);
function writeType(type: Type, flags: TypeFormatFlags) {
+ const nextFlags = flags & ~TypeFormatFlags.InTypeAlias;
// Write undefined/null type as any
if (type.flags & TypeFlags.Intrinsic) {
// Special handling for unknown / resolving types, they should show up as any and not unknown or __resolving
@@ -2078,23 +2127,35 @@ namespace ts {
writer.writeKeyword("this");
}
else if (type.flags & TypeFlags.Reference) {
- writeTypeReference(type, flags);
+ writeTypeReference(type, nextFlags);
+ }
+ else if (type.flags & TypeFlags.EnumLiteral) {
+ buildSymbolDisplay(getParentOfSymbol(type.symbol), writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
+ writePunctuation(writer, SyntaxKind.DotToken);
+ appendSymbolNameOnly(type.symbol, writer);
}
else if (type.flags & (TypeFlags.Class | TypeFlags.Interface | TypeFlags.Enum | TypeFlags.TypeParameter)) {
// The specified symbol flags need to be reinterpreted as type flags
- buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, flags);
+ buildSymbolDisplay(type.symbol, writer, enclosingDeclaration, SymbolFlags.Type, SymbolFormatFlags.None, nextFlags);
}
else if (type.flags & TypeFlags.Tuple) {
writeTupleType(type);
}
+ else if (!(flags & TypeFormatFlags.InTypeAlias) && type.flags & (TypeFlags.Anonymous | TypeFlags.UnionOrIntersection) && type.aliasSymbol) {
+ const typeArguments = type.aliasTypeArguments;
+ writeSymbolTypeReference(type.aliasSymbol, typeArguments, 0, typeArguments ? typeArguments.length : 0, nextFlags);
+ }
else if (type.flags & TypeFlags.UnionOrIntersection) {
- writeUnionOrIntersectionType(type, flags);
+ writeUnionOrIntersectionType(type, nextFlags);
}
else if (type.flags & TypeFlags.Anonymous) {
- writeAnonymousType(type, flags);
+ writeAnonymousType(type, nextFlags);
}
else if (type.flags & TypeFlags.StringLiteral) {
- writer.writeStringLiteral(`"${escapeString((type).text)}"`);
+ writer.writeStringLiteral(`"${escapeString((type).text)}"`);
+ }
+ else if (type.flags & TypeFlags.NumberLiteral) {
+ writer.writeStringLiteral((type).text);
}
else {
// Should never get here
@@ -2184,7 +2245,12 @@ namespace ts {
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.OpenParenToken);
}
- writeTypeList(type.types, type.flags & TypeFlags.Union ? SyntaxKind.BarToken : SyntaxKind.AmpersandToken);
+ if (type.flags & TypeFlags.Union) {
+ writeTypeList(formatUnionTypes(type.types), SyntaxKind.BarToken);
+ }
+ else {
+ writeTypeList(type.types, SyntaxKind.AmpersandToken);
+ }
if (flags & TypeFormatFlags.InElementType) {
writePunctuation(writer, SyntaxKind.CloseParenToken);
}
@@ -2900,7 +2966,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 && !(getCombinedTypeFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
+ if (strictNullChecks && declaration.initializer && !(getFalsyFlags(checkExpressionCached(declaration.initializer)) & TypeFlags.Undefined)) {
type = getTypeWithFacts(type, TypeFacts.NEUndefined);
}
return type;
@@ -2943,7 +3009,7 @@ namespace ts {
}
function addOptionality(type: Type, optional: boolean): Type {
- return strictNullChecks && optional ? addTypeKind(type, TypeFlags.Undefined) : type;
+ return strictNullChecks && optional ? includeFalsyTypes(type, TypeFlags.Undefined) : type;
}
// Return the inferred type for a variable, parameter, or property declaration
@@ -3017,7 +3083,7 @@ namespace ts {
// If the declaration specifies a binding pattern, use the type implied by the binding pattern
if (isBindingPattern(declaration.name)) {
- return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false);
+ return getTypeFromBindingPattern(declaration.name, /*includePatternInType*/ false, /*reportErrors*/ true);
}
// No type specified and nothing can be inferred
@@ -3027,24 +3093,22 @@ namespace ts {
// Return the type implied by a binding pattern element. This is the type of the initializer of the element if
// one is present. Otherwise, if the element is itself a binding pattern, it is the type implied by the binding
// pattern. Otherwise, it is the type any.
- function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean): Type {
+ function getTypeFromBindingElement(element: BindingElement, includePatternInType?: boolean, reportErrors?: boolean): Type {
if (element.initializer) {
- const type = checkExpressionCached(element.initializer);
- reportErrorsFromWidening(element, type);
- return getWidenedType(type);
+ return checkExpressionCached(element.initializer);
}
if (isBindingPattern(element.name)) {
- return getTypeFromBindingPattern(element.name, includePatternInType);
+ return getTypeFromBindingPattern(element.name, includePatternInType, reportErrors);
}
- if (compilerOptions.noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) {
+ if (reportErrors && compilerOptions.noImplicitAny && !declarationBelongsToPrivateAmbientMember(element)) {
reportImplicitAnyError(element, anyType);
}
return anyType;
}
// Return the type implied by an object binding pattern
- function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type {
- const members: SymbolTable = {};
+ function getTypeFromObjectBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
+ const members = createMap();
let hasComputedProperties = false;
forEach(pattern.elements, e => {
const name = e.propertyName || e.name;
@@ -3057,7 +3121,7 @@ namespace ts {
const text = getTextOfPropertyName(name);
const flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0);
const symbol = createSymbol(flags, text);
- symbol.type = getTypeFromBindingElement(e, includePatternInType);
+ symbol.type = getTypeFromBindingElement(e, includePatternInType, reportErrors);
symbol.bindingElement = e;
members[symbol.name] = symbol;
});
@@ -3072,13 +3136,13 @@ namespace ts {
}
// Return the type implied by an array binding pattern
- function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean): Type {
+ function getTypeFromArrayBindingPattern(pattern: BindingPattern, includePatternInType: boolean, reportErrors: boolean): Type {
const elements = pattern.elements;
if (elements.length === 0 || elements[elements.length - 1].dotDotDotToken) {
return languageVersion >= ScriptTarget.ES6 ? createIterableType(anyType) : anyArrayType;
}
// If the pattern has at least one element, and no rest element, then it should imply a tuple type.
- const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType));
+ const elementTypes = map(elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e, includePatternInType, reportErrors));
if (includePatternInType) {
const result = createNewTupleType(elementTypes);
result.pattern = pattern;
@@ -3094,10 +3158,10 @@ namespace ts {
// used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring
// parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of
// the parameter.
- function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean): Type {
+ function getTypeFromBindingPattern(pattern: BindingPattern, includePatternInType?: boolean, reportErrors?: boolean): Type {
return pattern.kind === SyntaxKind.ObjectBindingPattern
- ? getTypeFromObjectBindingPattern(pattern, includePatternInType)
- : getTypeFromArrayBindingPattern(pattern, includePatternInType);
+ ? getTypeFromObjectBindingPattern(pattern, includePatternInType, reportErrors)
+ : getTypeFromArrayBindingPattern(pattern, includePatternInType, reportErrors);
}
// Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type
@@ -3166,7 +3230,7 @@ namespace ts {
return unknownType;
}
- let type: Type = undefined;
+ let type: Type;
// Handle certain special assignment kinds, which happen to union across multiple declarations:
// * module.exports = expr
// * exports.p = expr
@@ -3174,13 +3238,20 @@ namespace ts {
// * className.prototype.method = expr
if (declaration.kind === SyntaxKind.BinaryExpression ||
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
- type = getUnionType(map(symbol.declarations,
- decl => decl.kind === SyntaxKind.BinaryExpression ?
- checkExpressionCached((decl).right) :
- checkExpressionCached((decl.parent).right)));
+ // Use JS Doc type if present on parent expression statement
+ if (declaration.flags & NodeFlags.JavaScriptFile) {
+ const typeTag = getJSDocTypeTag(declaration.parent);
+ if (typeTag && typeTag.typeExpression) {
+ return links.type = getTypeFromTypeNode(typeTag.typeExpression.type);
+ }
+ }
+ const declaredTypes = map(symbol.declarations,
+ decl => decl.kind === SyntaxKind.BinaryExpression ?
+ checkExpressionCached((decl).right) :
+ checkExpressionCached((decl.parent).right));
+ type = getUnionType(declaredTypes, /*subtypeReduction*/ true);
}
-
- if (type === undefined) {
+ else {
type = getWidenedTypeForVariableLikeDeclaration(declaration, /*reportErrors*/ true);
}
@@ -3292,7 +3363,7 @@ namespace ts {
else {
const type = createObjectType(TypeFlags.Anonymous, symbol);
links.type = strictNullChecks && symbol.flags & SymbolFlags.Optional ?
- addTypeKind(type, TypeFlags.Undefined) : type;
+ includeFalsyTypes(type, TypeFlags.Undefined) : type;
}
}
return links.type;
@@ -3602,7 +3673,7 @@ namespace ts {
const baseTypeNodes = getInterfaceBaseTypeNodes(declaration);
if (baseTypeNodes) {
for (const node of baseTypeNodes) {
- if (isSupportedExpressionWithTypeArguments(node)) {
+ if (isEntityNameExpression(node.expression)) {
const baseSymbol = resolveEntityName(node.expression, SymbolFlags.Type, /*ignoreErrors*/ true);
if (!baseSymbol || !(baseSymbol.flags & SymbolFlags.Interface) || getDeclaredTypeOfClassOrInterface(baseSymbol).thisType) {
return false;
@@ -3632,7 +3703,7 @@ namespace ts {
type.typeParameters = concatenate(outerTypeParameters, localTypeParameters);
type.outerTypeParameters = outerTypeParameters;
type.localTypeParameters = localTypeParameters;
- (type).instantiations = {};
+ (type).instantiations = createMap();
(type).instantiations[getTypeListId(type.typeParameters)] = type;
(type).target = type;
(type).typeArguments = type.typeParameters;
@@ -3653,8 +3724,9 @@ namespace ts {
return unknownType;
}
- let type: Type;
+ const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag);
+ let type: Type;
if (declaration) {
if (declaration.jsDocTypeLiteral) {
type = getTypeFromTypeNode(declaration.jsDocTypeLiteral);
@@ -3665,15 +3737,15 @@ namespace ts {
}
else {
declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration);
- type = getTypeFromTypeNode(declaration.type);
+ type = getTypeFromTypeNode(declaration.type, symbol, typeParameters);
}
if (popTypeResolution()) {
- links.typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
- if (links.typeParameters) {
+ links.typeParameters = typeParameters;
+ if (typeParameters) {
// Initialize the instantiation cache for generic type aliases. The declared type corresponds to
// an instantiation of the type alias with the type parameters supplied as type arguments.
- links.instantiations = {};
+ links.instantiations = createMap();
links.instantiations[getTypeListId(links.typeParameters)] = type;
}
}
@@ -3686,12 +3758,72 @@ namespace ts {
return links.declaredType;
}
+ function isLiteralEnumMember(symbol: Symbol, member: EnumMember) {
+ const expr = member.initializer;
+ if (!expr) {
+ return !isInAmbientContext(member);
+ }
+ return expr.kind === SyntaxKind.NumericLiteral ||
+ expr.kind === SyntaxKind.PrefixUnaryExpression && (expr).operator === SyntaxKind.MinusToken &&
+ (expr).operand.kind === SyntaxKind.NumericLiteral ||
+ expr.kind === SyntaxKind.Identifier && !!symbol.exports[(expr).text];
+ }
+
+ function enumHasLiteralMembers(symbol: Symbol) {
+ for (const declaration of symbol.declarations) {
+ if (declaration.kind === SyntaxKind.EnumDeclaration) {
+ for (const member of (declaration).members) {
+ if (!isLiteralEnumMember(symbol, member)) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+ }
+
function getDeclaredTypeOfEnum(symbol: Symbol): Type {
const links = getSymbolLinks(symbol);
if (!links.declaredType) {
- const type = createType(TypeFlags.Enum);
- type.symbol = symbol;
- links.declaredType = type;
+ const enumType = links.declaredType = createType(TypeFlags.Enum);
+ enumType.symbol = symbol;
+ if (enumHasLiteralMembers(symbol)) {
+ const memberTypeList: Type[] = [];
+ const memberTypes = createMap();
+ for (const declaration of enumType.symbol.declarations) {
+ if (declaration.kind === SyntaxKind.EnumDeclaration) {
+ computeEnumMemberValues(declaration);
+ for (const member of (declaration).members) {
+ const memberSymbol = getSymbolOfNode(member);
+ const value = getEnumMemberValue(member);
+ if (!memberTypes[value]) {
+ const memberType = memberTypes[value] = createType(TypeFlags.EnumLiteral);
+ memberType.symbol = memberSymbol;
+ memberType.baseType = enumType;
+ memberType.text = "" + value;
+ memberTypeList.push(memberType);
+ }
+ }
+ }
+ }
+ enumType.memberTypes = memberTypes;
+ if (memberTypeList.length > 1) {
+ enumType.flags |= TypeFlags.Union;
+ (enumType).types = memberTypeList;
+ unionTypes[getTypeListId(memberTypeList)] = enumType;
+ }
+ }
+ }
+ return links.declaredType;
+ }
+
+ function getDeclaredTypeOfEnumMember(symbol: Symbol): Type {
+ const links = getSymbolLinks(symbol);
+ if (!links.declaredType) {
+ const enumType = getDeclaredTypeOfEnum(getParentOfSymbol(symbol));
+ links.declaredType = enumType.flags & TypeFlags.Union ?
+ enumType.memberTypes[getEnumMemberValue(symbol.valueDeclaration)] :
+ enumType;
}
return links.declaredType;
}
@@ -3725,11 +3857,14 @@ namespace ts {
if (symbol.flags & SymbolFlags.TypeAlias) {
return getDeclaredTypeOfTypeAlias(symbol);
}
+ if (symbol.flags & SymbolFlags.TypeParameter) {
+ return getDeclaredTypeOfTypeParameter(symbol);
+ }
if (symbol.flags & SymbolFlags.Enum) {
return getDeclaredTypeOfEnum(symbol);
}
- if (symbol.flags & SymbolFlags.TypeParameter) {
- return getDeclaredTypeOfTypeParameter(symbol);
+ if (symbol.flags & SymbolFlags.EnumMember) {
+ return getDeclaredTypeOfEnumMember(symbol);
}
if (symbol.flags & SymbolFlags.Alias) {
return getDeclaredTypeOfAlias(symbol);
@@ -3763,7 +3898,7 @@ namespace ts {
case SyntaxKind.UndefinedKeyword:
case SyntaxKind.NullKeyword:
case SyntaxKind.NeverKeyword:
- case SyntaxKind.StringLiteralType:
+ case SyntaxKind.LiteralType:
return true;
case SyntaxKind.ArrayType:
return isIndependentType((node).elementType);
@@ -3817,7 +3952,7 @@ namespace ts {
}
function createSymbolTable(symbols: Symbol[]): SymbolTable {
- const result: SymbolTable = {};
+ const result = createMap();
for (const symbol of symbols) {
result[symbol.name] = symbol;
}
@@ -3827,7 +3962,7 @@ namespace ts {
// The mappingThisOnly flag indicates that the only type parameter being mapped is "this". When the flag is true,
// we check symbols to see if we can quickly conclude they are free of "this" references, thus needing no instantiation.
function createInstantiatedSymbolTable(symbols: Symbol[], mapper: TypeMapper, mappingThisOnly: boolean): SymbolTable {
- const result: SymbolTable = {};
+ const result = createMap();
for (const symbol of symbols) {
result[symbol.name] = mappingThisOnly && isIndependentMember(symbol) ? symbol : instantiateSymbol(symbol, mapper);
}
@@ -3836,7 +3971,7 @@ namespace ts {
function addInheritedMembers(symbols: SymbolTable, baseSymbols: Symbol[]) {
for (const s of baseSymbols) {
- if (!hasProperty(symbols, s.name)) {
+ if (!symbols[s.name]) {
symbols[s.name] = s;
}
}
@@ -3859,6 +3994,9 @@ namespace ts {
return createTypeReference((type).target,
concatenate((type).typeArguments, [thisArgument || (type).target.thisType]));
}
+ if (type.flags & TypeFlags.Tuple) {
+ return createTupleType((type as TupleType).elementTypes, thisArgument);
+ }
return type;
}
@@ -3908,7 +4046,7 @@ namespace ts {
}
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[],
- resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature {
+ resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasLiteralTypes: boolean): Signature {
const sig = new Signature(checker);
sig.declaration = declaration;
sig.typeParameters = typeParameters;
@@ -3918,20 +4056,20 @@ namespace ts {
sig.typePredicate = typePredicate;
sig.minArgumentCount = minArgumentCount;
sig.hasRestParameter = hasRestParameter;
- sig.hasStringLiterals = hasStringLiterals;
+ sig.hasLiteralTypes = hasLiteralTypes;
return sig;
}
function cloneSignature(sig: Signature): Signature {
return createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, sig.resolvedReturnType,
- sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals);
+ sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasLiteralTypes);
}
function getDefaultConstructSignatures(classType: InterfaceType): Signature[] {
const baseConstructorType = getBaseConstructorTypeOfClass(classType);
const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct);
if (baseSignatures.length === 0) {
- return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)];
+ return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false)];
}
const baseTypeNode = getBaseTypeNodeOfClass(classType);
const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode);
@@ -3950,7 +4088,7 @@ namespace ts {
}
function createTupleTypeMemberSymbols(memberTypes: Type[]): SymbolTable {
- const members: SymbolTable = {};
+ const members = createMap();
for (let i = 0; i < memberTypes.length; i++) {
const symbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "" + i);
symbol.type = memberTypes[i];
@@ -3960,9 +4098,10 @@ namespace ts {
}
function resolveTupleTypeMembers(type: TupleType) {
- const arrayElementType = getUnionType(type.elementTypes, /*noSubtypeReduction*/ true);
+ const arrayElementType = getUnionType(type.elementTypes);
// Make the tuple type itself the 'this' type by including an extra type argument
- const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type]));
+ // (Unless it's provided in the case that the tuple is a type parameter constraint)
+ const arrayType = resolveStructuredTypeMembers(createTypeFromGenericGlobalType(globalArrayType, [arrayElementType, type.thisType || type]));
const members = createTupleTypeMemberSymbols(type.elementTypes);
addInheritedMembers(members, arrayType.properties);
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexInfo, arrayType.numberIndexInfo);
@@ -4022,7 +4161,7 @@ namespace ts {
if (unionSignatures.length > 1) {
s = cloneSignature(signature);
if (forEach(unionSignatures, sig => sig.thisParameter)) {
- const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType));
+ const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType), /*subtypeReduction*/ true);
s.thisParameter = createTransientSymbol(signature.thisParameter, thisType);
}
// Clear resolved return type we possibly got from cloneSignature
@@ -4048,7 +4187,7 @@ namespace ts {
indexTypes.push(indexInfo.type);
isAnyReadonly = isAnyReadonly || indexInfo.isReadonly;
}
- return createIndexInfo(getUnionType(indexTypes), isAnyReadonly);
+ return createIndexInfo(getUnionType(indexTypes, /*subtypeReduction*/ true), isAnyReadonly);
}
function resolveUnionTypeMembers(type: UnionType) {
@@ -4172,11 +4311,9 @@ namespace ts {
function getPropertyOfObjectType(type: Type, name: string): Symbol {
if (type.flags & TypeFlags.ObjectType) {
const resolved = resolveStructuredTypeMembers(type);
- if (hasProperty(resolved.members, name)) {
- const symbol = resolved.members[name];
- if (symbolIsValue(symbol)) {
- return symbol;
- }
+ const symbol = resolved.members[name];
+ if (symbol && symbolIsValue(symbol)) {
+ return symbol;
}
}
}
@@ -4230,7 +4367,7 @@ namespace ts {
else if (type.flags & TypeFlags.NumberLike) {
type = globalNumberType;
}
- else if (type.flags & TypeFlags.Boolean) {
+ else if (type.flags & TypeFlags.BooleanLike) {
type = globalBooleanType;
}
else if (type.flags & TypeFlags.ESSymbol) {
@@ -4275,10 +4412,19 @@ namespace ts {
}
const propTypes: Type[] = [];
const declarations: Declaration[] = [];
+ let commonType: Type = undefined;
+ let hasCommonType = true;
for (const prop of props) {
if (prop.declarations) {
addRange(declarations, prop.declarations);
}
+ const type = getTypeOfSymbol(prop);
+ if (!commonType) {
+ commonType = type;
+ }
+ else if (type !== commonType) {
+ hasCommonType = false;
+ }
propTypes.push(getTypeOfSymbol(prop));
}
const result = createSymbol(
@@ -4288,6 +4434,7 @@ namespace ts {
commonFlags,
name);
result.containingType = containingType;
+ result.hasCommonType = hasCommonType;
result.declarations = declarations;
result.isReadonly = isReadonly;
result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
@@ -4295,29 +4442,32 @@ namespace ts {
}
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol {
- const properties = type.resolvedProperties || (type.resolvedProperties = {});
- if (hasProperty(properties, name)) {
- return properties[name];
- }
- const property = createUnionOrIntersectionProperty(type, name);
- if (property) {
- properties[name] = property;
+ const properties = type.resolvedProperties || (type.resolvedProperties = createMap());
+ let property = properties[name];
+ if (!property) {
+ property = createUnionOrIntersectionProperty(type, name);
+ if (property) {
+ properties[name] = property;
+ }
}
return property;
}
- // Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
- // necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
- // Object and Function as appropriate.
+ /**
+ * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
+ * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
+ * Object and Function as appropriate.
+ *
+ * @param type a type to look up property from
+ * @param name a name of property to look up in a given type
+ */
function getPropertyOfType(type: Type, name: string): Symbol {
type = getApparentType(type);
if (type.flags & TypeFlags.ObjectType) {
const resolved = resolveStructuredTypeMembers(type);
- if (hasProperty(resolved.members, name)) {
- const symbol = resolved.members[name];
- if (symbolIsValue(symbol)) {
- return symbol;
- }
+ const symbol = resolved.members[name];
+ if (symbol && symbolIsValue(symbol)) {
+ return symbol;
}
if (resolved === anyFunctionType || resolved.callSignatures.length || resolved.constructSignatures.length) {
const symbol = getPropertyOfObjectType(globalFunctionType, name);
@@ -4382,7 +4532,7 @@ namespace ts {
}
}
if (propTypes.length) {
- return getUnionType(propTypes);
+ return getUnionType(propTypes, /*subtypeReduction*/ true);
}
}
return undefined;
@@ -4479,7 +4629,7 @@ namespace ts {
const links = getNodeLinks(declaration);
if (!links.resolvedSignature) {
const parameters: Symbol[] = [];
- let hasStringLiterals = false;
+ let hasLiteralTypes = false;
let minArgumentCount = -1;
let thisParameter: Symbol = undefined;
let hasThisParameter: boolean;
@@ -4505,8 +4655,8 @@ namespace ts {
parameters.push(paramSymbol);
}
- if (param.type && param.type.kind === SyntaxKind.StringLiteralType) {
- hasStringLiterals = true;
+ if (param.type && param.type.kind === SyntaxKind.LiteralType) {
+ hasLiteralTypes = true;
}
if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) {
@@ -4549,7 +4699,7 @@ namespace ts {
createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) :
undefined;
- links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasStringLiterals);
+ links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasLiteralTypes);
}
return links.resolvedSignature;
}
@@ -4647,7 +4797,7 @@ namespace ts {
type = instantiateType(getReturnTypeOfSignature(signature.target), signature.mapper);
}
else if (signature.unionSignatures) {
- type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature));
+ type = getUnionType(map(signature.unionSignatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
}
else {
type = getReturnTypeFromBody(signature.declaration);
@@ -4698,7 +4848,7 @@ namespace ts {
// will result in a different declaration kind.
if (!signature.isolatedSignatureType) {
const isConstructor = signature.declaration.kind === SyntaxKind.Constructor || signature.declaration.kind === SyntaxKind.ConstructSignature;
- const type = createObjectType(TypeFlags.Anonymous | TypeFlags.FromSignature);
+ const type = createObjectType(TypeFlags.Anonymous);
type.members = emptySymbols;
type.properties = emptyArray;
type.callSignatures = !isConstructor ? [signature] : emptyArray;
@@ -4785,24 +4935,27 @@ namespace ts {
}
function getTypeListId(types: Type[]) {
+ let result = "";
if (types) {
- switch (types.length) {
- case 1:
- return "" + types[0].id;
- case 2:
- return types[0].id + "," + types[1].id;
- default:
- let result = "";
- for (let i = 0; i < types.length; i++) {
- if (i > 0) {
- result += ",";
- }
- result += types[i].id;
- }
- return result;
+ const length = types.length;
+ let i = 0;
+ while (i < length) {
+ const startId = types[i].id;
+ let count = 1;
+ while (i + count < length && types[i + count].id === startId + count) {
+ count++;
+ }
+ if (result.length) {
+ result += ",";
+ }
+ result += startId;
+ if (count > 1) {
+ result += ":" + count;
+ }
+ i += count;
}
}
- return "";
+ return result;
}
// This function is used to propagate certain flags when creating new object type references and union types.
@@ -4885,7 +5038,7 @@ namespace ts {
return getDeclaredTypeOfSymbol(symbol);
}
- function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): LeftHandSideExpression | EntityName {
+ function getTypeReferenceName(node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference): EntityNameOrEntityNameExpression | undefined {
switch (node.kind) {
case SyntaxKind.TypeReference:
return (node).typeName;
@@ -4894,8 +5047,9 @@ namespace ts {
case SyntaxKind.ExpressionWithTypeArguments:
// We only support expressions that are simple qualified names. For other
// expressions this produces undefined.
- if (isSupportedExpressionWithTypeArguments(node)) {
- return (node).expression;
+ const expr = (node).expression;
+ if (isEntityNameExpression(expr)) {
+ return expr;
}
// fall through;
@@ -4906,7 +5060,7 @@ namespace ts {
function resolveTypeReferenceName(
node: TypeReferenceNode | ExpressionWithTypeArguments | JSDocTypeReference,
- typeReferenceName: LeftHandSideExpression | EntityName) {
+ typeReferenceName: EntityNameExpression | EntityName) {
if (!typeReferenceName) {
return unknownSymbol;
@@ -4947,15 +5101,14 @@ namespace ts {
const typeReferenceName = getTypeReferenceName(node);
symbol = resolveTypeReferenceName(node, typeReferenceName);
type = getTypeReferenceType(node, symbol);
-
- links.resolvedSymbol = symbol;
- links.resolvedType = type;
}
else {
// We only support expressions that are simple qualified names. For other expressions this produces undefined.
- const typeNameOrExpression = node.kind === SyntaxKind.TypeReference ? (node).typeName :
- isSupportedExpressionWithTypeArguments(node) ? (node).expression :
- undefined;
+ const typeNameOrExpression: EntityNameOrEntityNameExpression = node.kind === SyntaxKind.TypeReference
+ ? (node).typeName
+ : isEntityNameExpression((node).expression)
+ ? (node).expression
+ : undefined;
symbol = typeNameOrExpression && resolveEntityName(typeNameOrExpression, SymbolFlags.Type) || unknownSymbol;
type = symbol === unknownSymbol ? unknownType :
symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface) ? getTypeFromClassOrInterfaceReference(node, symbol) :
@@ -5074,15 +5227,16 @@ namespace ts {
return links.resolvedType;
}
- function createTupleType(elementTypes: Type[]) {
- const id = getTypeListId(elementTypes);
- return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes));
+ function createTupleType(elementTypes: Type[], thisType?: Type) {
+ const id = getTypeListId(elementTypes) + "," + (thisType ? thisType.id : 0);
+ return tupleTypes[id] || (tupleTypes[id] = createNewTupleType(elementTypes, thisType));
}
- function createNewTupleType(elementTypes: Type[]) {
+ function createNewTupleType(elementTypes: Type[], thisType?: Type) {
const propagatedFlags = getPropagatingFlagsOfTypes(elementTypes, /*excludeKinds*/ 0);
const type = createObjectType(TypeFlags.Tuple | propagatedFlags);
type.elementTypes = elementTypes;
+ type.thisType = thisType;
return type;
}
@@ -5101,29 +5255,70 @@ namespace ts {
containsNonWideningType?: boolean;
}
- function addTypeToSet(typeSet: TypeSet, type: Type, typeSetKind: TypeFlags) {
- if (type.flags & typeSetKind) {
- addTypesToSet(typeSet, (type).types, typeSetKind);
+ function binarySearchTypes(types: Type[], type: Type): number {
+ let low = 0;
+ let high = types.length - 1;
+ const typeId = type.id;
+ while (low <= high) {
+ const middle = low + ((high - low) >> 1);
+ const id = types[middle].id;
+ if (id === typeId) {
+ return middle;
+ }
+ else if (id > typeId) {
+ high = middle - 1;
+ }
+ else {
+ low = middle + 1;
+ }
}
- else if (type.flags & (TypeFlags.Any | TypeFlags.Undefined | TypeFlags.Null)) {
- if (type.flags & TypeFlags.Any) typeSet.containsAny = true;
+ return ~low;
+ }
+
+ function containsType(types: Type[], type: Type): boolean {
+ return binarySearchTypes(types, type) >= 0;
+ }
+
+ function addTypeToUnion(typeSet: TypeSet, type: Type) {
+ if (type.flags & TypeFlags.Union) {
+ addTypesToUnion(typeSet, (type).types);
+ }
+ else if (type.flags & TypeFlags.Any) {
+ typeSet.containsAny = true;
+ }
+ else if (!strictNullChecks && type.flags & TypeFlags.Nullable) {
if (type.flags & TypeFlags.Undefined) typeSet.containsUndefined = true;
if (type.flags & TypeFlags.Null) typeSet.containsNull = true;
if (!(type.flags & TypeFlags.ContainsWideningType)) typeSet.containsNonWideningType = true;
}
- else if (type !== neverType && !contains(typeSet, type)) {
- typeSet.push(type);
+ else if (!(type.flags & TypeFlags.Never)) {
+ const len = typeSet.length;
+ const index = len && type.id > typeSet[len - 1].id ? ~len : binarySearchTypes(typeSet, type);
+ if (index < 0) {
+ if (!(type.flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method) && containsIdenticalType(typeSet, type))) {
+ typeSet.splice(~index, 0, type);
+ }
+ }
}
}
// Add the given types to the given type set. Order is preserved, duplicates are removed,
// and nested types of the given kind are flattened into the set.
- function addTypesToSet(typeSet: TypeSet, types: Type[], typeSetKind: TypeFlags) {
+ function addTypesToUnion(typeSet: TypeSet, types: Type[]) {
for (const type of types) {
- addTypeToSet(typeSet, type, typeSetKind);
+ addTypeToUnion(typeSet, type);
}
}
+ function containsIdenticalType(types: Type[], type: Type) {
+ for (const t of types) {
+ if (isTypeIdenticalTo(t, type)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
for (let i = 0, len = types.length; i < len; i++) {
if (candidate !== types[i] && isTypeSubtypeOf(candidate, types[i])) {
@@ -5143,14 +5338,14 @@ namespace ts {
}
}
- // We reduce the constituent type set to only include types that aren't subtypes of other types, unless
- // the noSubtypeReduction flag is specified, in which case we perform a simple deduplication based on
- // object identity. Subtype reduction is possible only when union types are known not to circularly
- // reference themselves (as is the case with union types created by expression constructs such as array
- // literals and the || and ?: operators). Named types can circularly reference themselves and therefore
- // cannot be deduplicated during their declaration. For example, "type Item = string | (() => Item" is
- // a named type that circularly references itself.
- function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
+ // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction
+ // flag is specified we also reduce the constituent type set to only include types that aren't subtypes
+ // of other types. Subtype reduction is expensive for large union types and is possible only when union
+ // types are known not to circularly reference themselves (as is the case with union types created by
+ // expression constructs such as array literals and the || and ?: operators). Named types can
+ // circularly reference themselves and therefore cannot be subtype reduced during their declaration.
+ // For example, "type Item = string | (() => Item" is a named type that circularly references itself.
+ function getUnionType(types: Type[], subtypeReduction?: boolean, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
if (types.length === 0) {
return neverType;
}
@@ -5158,15 +5353,11 @@ namespace ts {
return types[0];
}
const typeSet = [] as TypeSet;
- addTypesToSet(typeSet, types, TypeFlags.Union);
+ addTypesToUnion(typeSet, types);
if (typeSet.containsAny) {
return anyType;
}
- if (strictNullChecks) {
- if (typeSet.containsNull) typeSet.push(nullType);
- if (typeSet.containsUndefined) typeSet.push(undefinedType);
- }
- if (!noSubtypeReduction) {
+ if (subtypeReduction) {
removeSubtypes(typeSet);
}
if (typeSet.length === 0) {
@@ -5174,45 +5365,71 @@ namespace ts {
typeSet.containsUndefined ? typeSet.containsNonWideningType ? undefinedType : undefinedWideningType :
neverType;
}
- else if (typeSet.length === 1) {
- return typeSet[0];
+ return getUnionTypeFromSortedList(typeSet, aliasSymbol, aliasTypeArguments);
+ }
+
+ // This function assumes the constituent type list is sorted and deduplicated.
+ function getUnionTypeFromSortedList(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
+ if (types.length === 0) {
+ return neverType;
}
- const id = getTypeListId(typeSet);
+ if (types.length === 1) {
+ return types[0];
+ }
+ const id = getTypeListId(types);
let type = unionTypes[id];
if (!type) {
- const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable);
+ const propagatedFlags = getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable);
type = unionTypes[id] = createObjectType(TypeFlags.Union | propagatedFlags);
- type.types = typeSet;
+ type.types = types;
+ type.aliasSymbol = aliasSymbol;
+ type.aliasTypeArguments = aliasTypeArguments;
}
return type;
}
- function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
+ function getTypeFromUnionTypeNode(node: UnionTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
- links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction*/ true);
+ links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*subtypeReduction*/ false, aliasSymbol, aliasTypeArguments);
}
return links.resolvedType;
}
+ function addTypeToIntersection(typeSet: TypeSet, type: Type) {
+ if (type.flags & TypeFlags.Intersection) {
+ addTypesToIntersection(typeSet, (type).types);
+ }
+ else if (type.flags & TypeFlags.Any) {
+ typeSet.containsAny = true;
+ }
+ else if (!(type.flags & TypeFlags.Never) && (strictNullChecks || !(type.flags & TypeFlags.Nullable)) && !contains(typeSet, type)) {
+ typeSet.push(type);
+ }
+ }
+
+ // Add the given types to the given type set. Order is preserved, duplicates are removed,
+ // and nested types of the given kind are flattened into the set.
+ function addTypesToIntersection(typeSet: TypeSet, types: Type[]) {
+ for (const type of types) {
+ addTypeToIntersection(typeSet, type);
+ }
+ }
+
// We do not perform structural deduplication on intersection types. Intersection types are created only by the &
// type operator and we can't reduce those because we want to support recursive intersection types. For example,
// a type alias of the form "type List = T & { next: List }" cannot be reduced during its declaration.
// Also, unlike union types, the order of the constituent types is preserved in order that overload resolution
// for intersections of types with signatures can be deterministic.
- function getIntersectionType(types: Type[]): Type {
+ function getIntersectionType(types: Type[], aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
if (types.length === 0) {
return emptyObjectType;
}
const typeSet = [] as TypeSet;
- addTypesToSet(typeSet, types, TypeFlags.Intersection);
+ addTypesToIntersection(typeSet, types);
if (typeSet.containsAny) {
return anyType;
}
- if (strictNullChecks) {
- if (typeSet.containsNull) typeSet.push(nullType);
- if (typeSet.containsUndefined) typeSet.push(undefinedType);
- }
if (typeSet.length === 1) {
return typeSet[0];
}
@@ -5222,40 +5439,47 @@ namespace ts {
const propagatedFlags = getPropagatingFlagsOfTypes(typeSet, /*excludeKinds*/ TypeFlags.Nullable);
type = intersectionTypes[id] = createObjectType(TypeFlags.Intersection | propagatedFlags);
type.types = typeSet;
+ type.aliasSymbol = aliasSymbol;
+ type.aliasTypeArguments = aliasTypeArguments;
}
return type;
}
- function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type {
+ function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
- links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode));
+ links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode), aliasSymbol, aliasTypeArguments);
}
return links.resolvedType;
}
- function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node): Type {
+ function getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node: Node, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
// Deferred resolution of members is handled by resolveObjectTypeMembers
- links.resolvedType = createObjectType(TypeFlags.Anonymous, node.symbol);
+ const type = createObjectType(TypeFlags.Anonymous, node.symbol);
+ type.aliasSymbol = aliasSymbol;
+ type.aliasTypeArguments = aliasTypeArguments;
+ links.resolvedType = type;
}
return links.resolvedType;
}
- function getStringLiteralTypeForText(text: string): StringLiteralType {
- if (hasProperty(stringLiteralTypes, text)) {
- return stringLiteralTypes[text];
- }
- const type = stringLiteralTypes[text] = createType(TypeFlags.StringLiteral);
+ function createLiteralType(flags: TypeFlags, text: string) {
+ const type = createType(flags);
type.text = text;
return type;
}
- function getTypeFromStringLiteralTypeNode(node: StringLiteralTypeNode): Type {
+ function getLiteralTypeForText(flags: TypeFlags, text: string) {
+ const map = flags & TypeFlags.StringLiteral ? stringLiteralTypes : numericLiteralTypes;
+ return map[text] || (map[text] = createLiteralType(flags, text));
+ }
+
+ function getTypeFromLiteralTypeNode(node: LiteralTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
- links.resolvedType = getStringLiteralTypeForText(unescapeIdentifier(node.text));
+ links.resolvedType = checkExpression(node.literal);
}
return links.resolvedType;
}
@@ -5299,7 +5523,7 @@ namespace ts {
return links.resolvedType;
}
- function getTypeFromTypeNode(node: TypeNode): Type {
+ function getTypeFromTypeNode(node: TypeNode, aliasSymbol?: Symbol, aliasTypeArguments?: Type[]): Type {
switch (node.kind) {
case SyntaxKind.AnyKeyword:
case SyntaxKind.JSDocAllType:
@@ -5324,8 +5548,10 @@ namespace ts {
case SyntaxKind.ThisType:
case SyntaxKind.ThisKeyword:
return getTypeFromThisTypeNode(node);
- case SyntaxKind.StringLiteralType:
- return getTypeFromStringLiteralTypeNode(node);
+ case SyntaxKind.LiteralType:
+ return getTypeFromLiteralTypeNode(node);
+ case SyntaxKind.JSDocLiteralType:
+ return getTypeFromLiteralTypeNode((node).literal);
case SyntaxKind.TypeReference:
case SyntaxKind.JSDocTypeReference:
return getTypeFromTypeReference(node);
@@ -5342,9 +5568,9 @@ namespace ts {
return getTypeFromTupleTypeNode(node);
case SyntaxKind.UnionType:
case SyntaxKind.JSDocUnionType:
- return getTypeFromUnionTypeNode(node);
+ return getTypeFromUnionTypeNode(node, aliasSymbol, aliasTypeArguments);
case SyntaxKind.IntersectionType:
- return getTypeFromIntersectionTypeNode(node);
+ return getTypeFromIntersectionTypeNode(node, aliasSymbol, aliasTypeArguments);
case SyntaxKind.ParenthesizedType:
case SyntaxKind.JSDocNullableType:
case SyntaxKind.JSDocNonNullableType:
@@ -5358,7 +5584,7 @@ namespace ts {
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.JSDocRecordType:
- return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
+ return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node, aliasSymbol, aliasTypeArguments);
// This function assumes that an identifier or qualified name is a type expression
// Callers should first ensure this by calling isTypeNode
case SyntaxKind.Identifier:
@@ -5411,6 +5637,7 @@ namespace ts {
count == 2 ? createBinaryTypeMapper(sources[0], targets ? targets[0] : anyType, sources[1], targets ? targets[1] : anyType) :
createArrayTypeMapper(sources, targets);
mapper.mappedTypes = sources;
+ mapper.targetTypes = targets;
return mapper;
}
@@ -5492,7 +5719,7 @@ namespace ts {
instantiateList(signature.parameters, mapper, instantiateSymbol),
instantiateType(signature.resolvedReturnType, mapper),
freshTypePredicate,
- signature.minArgumentCount, signature.hasRestParameter, signature.hasStringLiterals);
+ signature.minArgumentCount, signature.hasRestParameter, signature.hasLiteralTypes);
result.target = signature;
result.mapper = mapper;
return result;
@@ -5536,6 +5763,8 @@ namespace ts {
const result = createObjectType(TypeFlags.Anonymous | TypeFlags.Instantiated, type.symbol);
result.target = type;
result.mapper = mapper;
+ result.aliasSymbol = type.aliasSymbol;
+ result.aliasTypeArguments = mapper.targetTypes;
mapper.instantiations[type.id] = result;
return result;
}
@@ -5612,11 +5841,11 @@ namespace ts {
if (type.flags & TypeFlags.Tuple) {
return createTupleType(instantiateList((type).elementTypes, mapper, instantiateType));
}
- if (type.flags & TypeFlags.Union) {
- return getUnionType(instantiateList((type).types, mapper, instantiateType), /*noSubtypeReduction*/ true);
+ if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Primitive)) {
+ return getUnionType(instantiateList((type).types, mapper, instantiateType), /*subtypeReduction*/ false, type.aliasSymbol, mapper.targetTypes);
}
if (type.flags & TypeFlags.Intersection) {
- return getIntersectionType(instantiateList((type).types, mapper, instantiateType));
+ return getIntersectionType(instantiateList((type).types, mapper, instantiateType), type.aliasSymbol, mapper.targetTypes);
}
}
return type;
@@ -5684,23 +5913,30 @@ namespace ts {
// TYPE CHECKING
function isTypeIdenticalTo(source: Type, target: Type): boolean {
- return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined);
+ return isTypeRelatedTo(source, target, identityRelation);
}
function compareTypesIdentical(source: Type, target: Type): Ternary {
- return checkTypeRelatedTo(source, target, identityRelation, /*errorNode*/ undefined) ? Ternary.True : Ternary.False;
+ return isTypeRelatedTo(source, target, identityRelation) ? Ternary.True : Ternary.False;
}
function compareTypesAssignable(source: Type, target: Type): Ternary {
- return checkTypeRelatedTo(source, target, assignableRelation, /*errorNode*/ undefined) ? Ternary.True : Ternary.False;
+ return isTypeRelatedTo(source, target, assignableRelation) ? Ternary.True : Ternary.False;
}
function isTypeSubtypeOf(source: Type, target: Type): boolean {
- return checkTypeSubtypeOf(source, target, /*errorNode*/ undefined);
+ return isTypeRelatedTo(source, target, subtypeRelation);
}
function isTypeAssignableTo(source: Type, target: Type): boolean {
- return checkTypeAssignableTo(source, target, /*errorNode*/ undefined);
+ return isTypeRelatedTo(source, target, assignableRelation);
+ }
+
+ // A type S is considered to be an instance of a type T if S and T are the same type or if S is a
+ // subtype of T but not structurally identical to T. This specifically means that two distinct but
+ // structurally identical types (such as two classes) are not considered instances of each other.
+ function isTypeInstanceOf(source: Type, target: Type): boolean {
+ return source === target || isTypeSubtypeOf(source, target) && !isTypeIdenticalTo(source, target);
}
/**
@@ -5708,7 +5944,7 @@ namespace ts {
* If one needs to check both directions for comparability, use a second call to this function or 'checkTypeComparableTo'.
*/
function isTypeComparableTo(source: Type, target: Type): boolean {
- return checkTypeComparableTo(source, target, /*errorNode*/ undefined);
+ return isTypeRelatedTo(source, target, comparableRelation);
}
function areTypesComparable(type1: Type, type2: Type): boolean {
@@ -5737,6 +5973,8 @@ namespace ts {
return compareSignaturesRelated(source, target, ignoreReturnTypes, /*reportErrors*/ false, /*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
}
+ type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
+
/**
* See signatureRelatedTo, compareSignaturesIdentical
*/
@@ -5744,7 +5982,7 @@ namespace ts {
target: Signature,
ignoreReturnTypes: boolean,
reportErrors: boolean,
- errorReporter: (d: DiagnosticMessage, arg0?: string, arg1?: string) => void,
+ errorReporter: ErrorReporter,
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
// TODO (drosen): De-duplicate code between related functions.
if (source === target) {
@@ -5829,7 +6067,7 @@ namespace ts {
function compareTypePredicateRelatedTo(source: TypePredicate,
target: TypePredicate,
reportErrors: boolean,
- errorReporter: (d: DiagnosticMessage, arg0?: string, arg1?: string) => void,
+ errorReporter: ErrorReporter,
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
if (source.kind !== target.kind) {
if (reportErrors) {
@@ -5866,8 +6104,8 @@ namespace ts {
const sourceReturnType = getReturnTypeOfSignature(erasedSource);
const targetReturnType = getReturnTypeOfSignature(erasedTarget);
if (targetReturnType === voidType
- || checkTypeRelatedTo(targetReturnType, sourceReturnType, assignableRelation, /*errorNode*/ undefined)
- || checkTypeRelatedTo(sourceReturnType, targetReturnType, assignableRelation, /*errorNode*/ undefined)) {
+ || isTypeRelatedTo(targetReturnType, sourceReturnType, assignableRelation)
+ || isTypeRelatedTo(sourceReturnType, targetReturnType, assignableRelation)) {
return isSignatureAssignableTo(erasedSource, erasedTarget, /*ignoreReturnTypes*/ true);
}
@@ -5901,6 +6139,64 @@ namespace ts {
}
}
+ function isEnumTypeRelatedTo(source: EnumType, target: EnumType, errorReporter?: ErrorReporter) {
+ if (source === target) {
+ return true;
+ }
+ if (source.symbol.name !== target.symbol.name || !(source.symbol.flags & SymbolFlags.RegularEnum) || !(target.symbol.flags & SymbolFlags.RegularEnum)) {
+ return false;
+ }
+ const targetEnumType = getTypeOfSymbol(target.symbol);
+ for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) {
+ if (property.flags & SymbolFlags.EnumMember) {
+ const targetProperty = getPropertyOfType(targetEnumType, property.name);
+ if (!targetProperty || !(targetProperty.flags & SymbolFlags.EnumMember)) {
+ if (errorReporter) {
+ errorReporter(Diagnostics.Property_0_is_missing_in_type_1, property.name,
+ typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType));
+ }
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map, errorReporter?: ErrorReporter) {
+ if (target.flags & TypeFlags.Never) return false;
+ if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return true;
+ if (source.flags & TypeFlags.StringLike && target.flags & TypeFlags.String) return true;
+ if (source.flags & TypeFlags.NumberLike && target.flags & TypeFlags.Number) return true;
+ if (source.flags & TypeFlags.BooleanLike && target.flags & TypeFlags.Boolean) return true;
+ if (source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.Enum && (source).baseType === target) return true;
+ if (source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum && isEnumTypeRelatedTo(source, target, errorReporter)) return true;
+ if (source.flags & TypeFlags.Undefined && (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void))) return true;
+ if (source.flags & TypeFlags.Null && (!strictNullChecks || target.flags & TypeFlags.Null)) return true;
+ if (relation === assignableRelation || relation === comparableRelation) {
+ if (source.flags & TypeFlags.Any) return true;
+ if (source.flags & (TypeFlags.Number | TypeFlags.NumberLiteral) && target.flags & TypeFlags.Enum) return true;
+ if (source.flags & TypeFlags.NumberLiteral && target.flags & TypeFlags.EnumLiteral && (source).text === (target).text) return true;
+ }
+ return false;
+ }
+
+ function isTypeRelatedTo(source: Type, target: Type, relation: Map) {
+ if (source === target || relation !== identityRelation && isSimpleTypeRelatedTo(source, target, relation)) {
+ return true;
+ }
+ if (source.flags & TypeFlags.ObjectType && target.flags & TypeFlags.ObjectType) {
+ const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
+ const related = relation[id];
+ if (related !== undefined) {
+ return related === RelationComparisonResult.Succeeded;
+ }
+ }
+ if (source.flags & TypeFlags.StructuredOrTypeParameter || target.flags & TypeFlags.StructuredOrTypeParameter) {
+ return checkTypeRelatedTo(source, target, relation, undefined, undefined, undefined);
+ }
+ return false;
+ }
+
/**
* Checks if 'source' is related to 'target' (e.g.: is a assignable to).
* @param source The left-hand-side of the relation.
@@ -5972,33 +6268,12 @@ namespace ts {
let result: Ternary;
// both types are the same - covers 'they are the same primitive type or both are Any' or the same type parameter cases
if (source === target) return Ternary.True;
+
if (relation === identityRelation) {
return isIdenticalTo(source, target);
}
- if (!(target.flags & TypeFlags.Never)) {
- if (target.flags & TypeFlags.Any || source.flags & TypeFlags.Never) return Ternary.True;
- if (source.flags & TypeFlags.Undefined) {
- if (!strictNullChecks || target.flags & (TypeFlags.Undefined | TypeFlags.Void)) return Ternary.True;
- }
- if (source.flags & TypeFlags.Null) {
- if (!strictNullChecks || target.flags & TypeFlags.Null) return Ternary.True;
- }
- if (source.flags & TypeFlags.Enum && target === numberType) return Ternary.True;
- if (source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum) {
- if (result = enumRelatedTo(source, target, reportErrors)) {
- return result;
- }
- }
- if (source.flags & TypeFlags.StringLiteral && target === stringType) return Ternary.True;
- if (relation === assignableRelation || relation === comparableRelation) {
- if (source.flags & TypeFlags.Any) return Ternary.True;
- if (source === numberType && target.flags & TypeFlags.Enum) return Ternary.True;
- }
- if (source.flags & TypeFlags.Boolean && target.flags & TypeFlags.Boolean) {
- return Ternary.True;
- }
- }
+ if (isSimpleTypeRelatedTo(source, target, relation, reportErrors ? reportError : undefined)) return Ternary.True;
if (source.flags & TypeFlags.FreshObjectLiteral) {
if (hasExcessProperties(source, target, reportErrors)) {
@@ -6021,10 +6296,10 @@ namespace ts {
// Note that these checks are specifically ordered to produce correct results.
if (source.flags & TypeFlags.Union) {
if (relation === comparableRelation) {
- result = someTypeRelatedToType(source as UnionType, target, reportErrors);
+ result = someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive));
}
else {
- result = eachTypeRelatedToType(source as UnionType, target, reportErrors);
+ result = eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive));
}
if (result) {
@@ -6061,7 +6336,7 @@ namespace ts {
}
}
if (target.flags & TypeFlags.Union) {
- if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive))) {
+ if (result = typeRelatedToSomeType(source, target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive))) {
return result;
}
}
@@ -6202,18 +6477,10 @@ namespace ts {
function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary {
const targetTypes = target.types;
- let len = targetTypes.length;
- // The null and undefined types are guaranteed to be at the end of the constituent type list. In order
- // to produce the best possible errors we first check the nullable types, such that the last type we
- // check and report errors from is a non-nullable type if one is present.
- while (len >= 2 && targetTypes[len - 1].flags & TypeFlags.Nullable) {
- const related = isRelatedTo(source, targetTypes[len - 1], /*reportErrors*/ false);
- if (related) {
- return related;
- }
- len--;
+ if (target.flags & TypeFlags.Union && containsType(targetTypes, source)) {
+ return Ternary.True;
}
- // Now check the non-nullable types and report errors on the last one.
+ const len = targetTypes.length;
for (let i = 0; i < len; i++) {
const related = isRelatedTo(source, targetTypes[i], reportErrors && i === len - 1);
if (related) {
@@ -6238,18 +6505,10 @@ namespace ts {
function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
const sourceTypes = source.types;
- let len = sourceTypes.length;
- // The null and undefined types are guaranteed to be at the end of the constituent type list. In order
- // to produce the best possible errors we first check the nullable types, such that the last type we
- // check and report errors from is a non-nullable type if one is present.
- while (len >= 2 && sourceTypes[len - 1].flags & TypeFlags.Nullable) {
- const related = isRelatedTo(sourceTypes[len - 1], target, /*reportErrors*/ false);
- if (related) {
- return related;
- }
- len--;
+ if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) {
+ return Ternary.True;
}
- // Now check the non-nullable types and report errors on the last one.
+ const len = sourceTypes.length;
for (let i = 0; i < len; i++) {
const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1);
if (related) {
@@ -6331,7 +6590,7 @@ namespace ts {
}
sourceStack[depth] = source;
targetStack[depth] = target;
- maybeStack[depth] = {};
+ maybeStack[depth] = createMap();
maybeStack[depth][id] = RelationComparisonResult.Succeeded;
depth++;
const saveExpandingFlags = expandingFlags;
@@ -6362,7 +6621,7 @@ namespace ts {
const maybeCache = maybeStack[depth];
// If result is definitely true, copy assumptions to global cache, else copy to next level up
const destinationCache = (result === Ternary.True || depth === 0) ? relation : maybeStack[depth - 1];
- copyMap(maybeCache, destinationCache);
+ copyProperties(maybeCache, destinationCache);
}
else {
// A false result goes straight into global cache (when something is false under assumptions it
@@ -6626,29 +6885,6 @@ namespace ts {
return Ternary.False;
}
- function enumRelatedTo(source: Type, target: Type, reportErrors?: boolean) {
- if (source.symbol.name !== target.symbol.name ||
- source.symbol.flags & SymbolFlags.ConstEnum ||
- target.symbol.flags & SymbolFlags.ConstEnum) {
- return Ternary.False;
- }
- const targetEnumType = getTypeOfSymbol(target.symbol);
- for (const property of getPropertiesOfType(getTypeOfSymbol(source.symbol))) {
- if (property.flags & SymbolFlags.EnumMember) {
- const targetProperty = getPropertyOfType(targetEnumType, property.name);
- if (!targetProperty || !(targetProperty.flags & SymbolFlags.EnumMember)) {
- if (reportErrors) {
- reportError(Diagnostics.Property_0_is_missing_in_type_1,
- property.name,
- typeToString(target, /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType));
- }
- return Ternary.False;
- }
- }
- }
- return Ternary.True;
- }
-
function constructorVisibilitiesAreCompatible(sourceSignature: Signature, targetSignature: Signature, reportErrors: boolean) {
if (!sourceSignature.declaration || !targetSignature.declaration) {
return true;
@@ -6758,9 +6994,11 @@ namespace ts {
// A source signature partially matches a target signature if the target signature has no fewer required
// parameters and no more overall parameters than the source signature (where a signature with a rest
// parameter is always considered to have more overall parameters than one without).
+ const sourceRestCount = source.hasRestParameter ? 1 : 0;
+ const targetRestCount = target.hasRestParameter ? 1 : 0;
if (partialMatch && source.minArgumentCount <= target.minArgumentCount && (
- source.hasRestParameter && !target.hasRestParameter ||
- source.hasRestParameter === target.hasRestParameter && source.parameters.length >= target.parameters.length)) {
+ sourceRestCount > targetRestCount ||
+ sourceRestCount === targetRestCount && source.parameters.length >= target.parameters.length)) {
return true;
}
return false;
@@ -6832,24 +7070,16 @@ namespace ts {
return true;
}
- function getCombinedFlagsOfTypes(types: Type[]) {
- let flags: TypeFlags = 0;
- for (const t of types) {
- flags |= t.flags;
- }
- return flags;
- }
-
function getCommonSupertype(types: Type[]): Type {
if (!strictNullChecks) {
return forEach(types, t => isSupertypeOfEach(t, types) ? t : undefined);
}
const primaryTypes = filter(types, t => !(t.flags & TypeFlags.Nullable));
if (!primaryTypes.length) {
- return getUnionType(types);
+ return getUnionType(types, /*subtypeReduction*/ true);
}
const supertype = forEach(primaryTypes, t => isSupertypeOfEach(t, primaryTypes) ? t : undefined);
- return supertype && addTypeKind(supertype, getCombinedFlagsOfTypes(types) & TypeFlags.Nullable);
+ return supertype && includeFalsyTypes(supertype, getFalsyFlagsOfTypes(types) & TypeFlags.Nullable);
}
function reportNoCommonSupertypeError(types: Type[], errorLocation: Node, errorMessageChainHead: DiagnosticMessageChain): void {
@@ -6908,10 +7138,23 @@ namespace ts {
return !!getPropertyOfType(type, "0");
}
- function isStringLiteralUnionType(type: Type): boolean {
- return type.flags & TypeFlags.StringLiteral ? true :
- type.flags & TypeFlags.Union ? forEach((type).types, isStringLiteralUnionType) :
- false;
+ function isUnitType(type: Type): boolean {
+ return (type.flags & (TypeFlags.Literal | TypeFlags.Undefined | TypeFlags.Null)) !== 0;
+ }
+
+ function isUnitUnionType(type: Type): boolean {
+ return type.flags & TypeFlags.Boolean ? true :
+ type.flags & TypeFlags.Union ? type.flags & TypeFlags.Enum ? true : !forEach((type).types, t => !isUnitType(t)) :
+ isUnitType(type);
+ }
+
+ function getBaseTypeOfUnitType(type: Type): Type {
+ return type.flags & TypeFlags.StringLiteral ? stringType :
+ type.flags & TypeFlags.NumberLiteral ? numberType :
+ type.flags & TypeFlags.BooleanLiteral ? booleanType :
+ type.flags & TypeFlags.EnumLiteral ? (type).baseType :
+ type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum) ? getUnionType(map((type).types, getBaseTypeOfUnitType)) :
+ type;
}
/**
@@ -6922,22 +7165,43 @@ namespace ts {
return !!(type.flags & TypeFlags.Tuple);
}
- function getCombinedTypeFlags(type: Type): TypeFlags {
- return type.flags & TypeFlags.Union ? getCombinedFlagsOfTypes((type).types) : type.flags;
+ function getFalsyFlagsOfTypes(types: Type[]): TypeFlags {
+ let result: TypeFlags = 0;
+ for (const t of types) {
+ result |= getFalsyFlags(t);
+ }
+ return result;
}
- function addTypeKind(type: Type, kind: TypeFlags) {
- if ((getCombinedTypeFlags(type) & kind) === kind) {
+ // Returns the String, Number, Boolean, StringLiteral, NumberLiteral, BooleanLiteral, Void, Undefined, or Null
+ // flags for the string, number, boolean, "", 0, false, void, undefined, or null types respectively. Returns
+ // no flags for all other types (including non-falsy literal types).
+ function getFalsyFlags(type: Type): TypeFlags {
+ return type.flags & TypeFlags.Union ? getFalsyFlagsOfTypes((type).types) :
+ type.flags & TypeFlags.StringLiteral ? type === emptyStringType ? TypeFlags.StringLiteral : 0 :
+ type.flags & TypeFlags.NumberLiteral ? type === zeroType ? TypeFlags.NumberLiteral : 0 :
+ type.flags & TypeFlags.BooleanLiteral ? type === falseType ? TypeFlags.BooleanLiteral : 0 :
+ type.flags & TypeFlags.PossiblyFalsy;
+ }
+
+ function includeFalsyTypes(type: Type, flags: TypeFlags) {
+ if ((getFalsyFlags(type) & flags) === flags) {
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);
+ if (flags & TypeFlags.StringLike) types.push(emptyStringType);
+ if (flags & TypeFlags.NumberLike) types.push(zeroType);
+ if (flags & TypeFlags.BooleanLike) types.push(falseType);
+ if (flags & TypeFlags.Void) types.push(voidType);
+ if (flags & TypeFlags.Undefined) types.push(undefinedType);
+ if (flags & TypeFlags.Null) types.push(nullType);
+ return getUnionType(types, /*subtypeReduction*/ true);
+ }
+
+ function removeDefinitelyFalsyTypes(type: Type): Type {
+ return getFalsyFlags(type) & TypeFlags.DefinitelyFalsy ?
+ filterType(type, t => !(getFalsyFlags(t) & TypeFlags.DefinitelyFalsy)) :
+ type;
}
function getNonNullableType(type: Type): Type {
@@ -6967,7 +7231,7 @@ namespace ts {
}
function transformTypeOfMembers(type: Type, f: (propertyType: Type) => Type) {
- const members: SymbolTable = {};
+ const members = createMap();
for (const property of getPropertiesOfObjectType(type)) {
const original = getTypeOfSymbol(property);
const updated = f(original);
@@ -7028,7 +7292,7 @@ namespace ts {
return getWidenedTypeOfObjectLiteral(type);
}
if (type.flags & TypeFlags.Union) {
- return getUnionType(map((type).types, getWidenedConstituentType), /*noSubtypeReduction*/ true);
+ return getUnionType(map((type).types, getWidenedConstituentType));
}
if (isArrayType(type)) {
return createArrayType(getWidenedType((type).typeArguments[0]));
@@ -7168,12 +7432,30 @@ namespace ts {
};
}
+ // Return true if the given type could possibly reference a type parameter for which
+ // we perform type inference (i.e. a type parameter of a generic function). We cache
+ // results for union and intersection types for performance reasons.
+ function couldContainTypeParameters(type: Type): boolean {
+ return !!(type.flags & TypeFlags.TypeParameter ||
+ type.flags & TypeFlags.Reference && forEach((type).typeArguments, couldContainTypeParameters) ||
+ type.flags & TypeFlags.Tuple && forEach((type).elementTypes, couldContainTypeParameters) ||
+ type.flags & TypeFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class) ||
+ type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeParameters(type));
+ }
+
+ function couldUnionOrIntersectionContainTypeParameters(type: UnionOrIntersectionType): boolean {
+ if (type.couldContainTypeParameters === undefined) {
+ type.couldContainTypeParameters = forEach(type.types, couldContainTypeParameters);
+ }
+ return type.couldContainTypeParameters;
+ }
+
function inferTypes(context: InferenceContext, source: Type, target: Type) {
let sourceStack: Type[];
let targetStack: Type[];
let depth = 0;
let inferiority = 0;
- const visited: Map = {};
+ const visited = createMap();
inferFromTypes(source, target);
function isInProcess(source: Type, target: Type) {
@@ -7186,12 +7468,22 @@ namespace ts {
}
function inferFromTypes(source: Type, target: Type) {
- if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union ||
+ if (!couldContainTypeParameters(target)) {
+ return;
+ }
+ if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union && !(source.flags & TypeFlags.Enum && target.flags & TypeFlags.Enum) ||
source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
- // Source and target are both unions or both intersections. First, find each
- // target constituent type that has an identically matching source constituent
- // type, and for each such target constituent type infer from the type to itself.
- // When inferring from a type to itself we effectively find all type parameter
+ // Source and target are both unions or both intersections. If source and target
+ // are the same type, just relate each constituent type to itself.
+ if (source === target) {
+ for (const t of (source).types) {
+ inferFromTypes(t, t);
+ }
+ return;
+ }
+ // Find each target constituent type that has an identically matching source
+ // constituent type, and for each such target constituent type infer from the type to
+ // itself. When inferring from a type to itself we effectively find all type parameter
// occurrences within that type and infer themselves as their type arguments.
let matchingTypes: Type[];
for (const t of (target).types) {
@@ -7289,25 +7581,18 @@ namespace ts {
}
else {
source = getApparentType(source);
- if (source.flags & TypeFlags.ObjectType && (
- target.flags & TypeFlags.Reference && (target).typeArguments ||
- target.flags & TypeFlags.Tuple ||
- target.flags & TypeFlags.Anonymous && target.symbol && target.symbol.flags & (SymbolFlags.Method | SymbolFlags.TypeLiteral | SymbolFlags.Class))) {
- // If source is an object type, and target is a type reference with type arguments, a tuple type,
- // the type of a method, or a type literal, infer from members
+ if (source.flags & TypeFlags.ObjectType) {
if (isInProcess(source, target)) {
return;
}
if (isDeeplyNestedGeneric(source, sourceStack, depth) && isDeeplyNestedGeneric(target, targetStack, depth)) {
return;
}
-
const key = source.id + "," + target.id;
- if (hasProperty(visited, key)) {
+ if (visited[key]) {
return;
}
visited[key] = true;
-
if (depth === 0) {
sourceStack = [];
targetStack = [];
@@ -7397,7 +7682,7 @@ namespace ts {
reducedTypes.push(t);
}
}
- return type.flags & TypeFlags.Union ? getUnionType(reducedTypes, /*noSubtypeReduction*/ true) : getIntersectionType(reducedTypes);
+ return type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes);
}
function getInferenceCandidates(context: InferenceContext, index: number): Type[] {
@@ -7412,7 +7697,7 @@ namespace ts {
const inferences = getInferenceCandidates(context, index);
if (inferences.length) {
// Infer widened union or supertype, or the unknown type for no common supertype
- const unionOrSuperType = context.inferUnionTypes ? getUnionType(inferences) : getCommonSupertype(inferences);
+ const unionOrSuperType = context.inferUnionTypes ? getUnionType(inferences, /*subtypeReduction*/ true) : getCommonSupertype(inferences);
inferredType = unionOrSuperType ? getWidenedType(unionOrSuperType) : unknownType;
inferenceSucceeded = !!unionOrSuperType;
}
@@ -7501,11 +7786,6 @@ namespace ts {
return undefined;
}
- function isNullOrUndefinedLiteral(node: Expression) {
- return node.kind === SyntaxKind.NullKeyword ||
- node.kind === SyntaxKind.Identifier && getResolvedSymbol(node) === undefinedSymbol;
- }
-
function getLeftmostIdentifierOrThis(node: Node): Node {
switch (node.kind) {
case SyntaxKind.Identifier:
@@ -7518,16 +7798,17 @@ namespace ts {
}
function isMatchingReference(source: Node, target: Node): boolean {
- if (source.kind === target.kind) {
- switch (source.kind) {
- case SyntaxKind.Identifier:
- return getResolvedSymbol(source) === getResolvedSymbol(target);
- case SyntaxKind.ThisKeyword:
- return true;
- case SyntaxKind.PropertyAccessExpression:
- return (source).name.text === (target).name.text &&
- isMatchingReference((source).expression, (target).expression);
- }
+ switch (source.kind) {
+ case SyntaxKind.Identifier:
+ return target.kind === SyntaxKind.Identifier && getResolvedSymbol(source) === getResolvedSymbol(target) ||
+ (target.kind === SyntaxKind.VariableDeclaration || target.kind === SyntaxKind.BindingElement) &&
+ getExportSymbolOfValueSymbolIfExported(getResolvedSymbol(source)) === getSymbolOfNode(target);
+ case SyntaxKind.ThisKeyword:
+ return target.kind === SyntaxKind.ThisKeyword;
+ case SyntaxKind.PropertyAccessExpression:
+ return target.kind === SyntaxKind.PropertyAccessExpression &&
+ (source).name.text === (target).name.text &&
+ isMatchingReference((source).expression, (target).expression);
}
return false;
}
@@ -7542,6 +7823,51 @@ namespace ts {
return false;
}
+ // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared
+ // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property
+ // a possible discriminant if its type differs in the constituents of containing union type, and if every
+ // choice is a unit type or a union of unit types.
+ function containsMatchingReferenceDiscriminant(source: Node, target: Node) {
+ return target.kind === SyntaxKind.PropertyAccessExpression &&
+ containsMatchingReference(source, (target).expression) &&
+ isDiscriminantProperty(getDeclaredTypeOfReference((target).expression), (target).name.text);
+ }
+
+ function getDeclaredTypeOfReference(expr: Node): Type {
+ if (expr.kind === SyntaxKind.Identifier) {
+ return getTypeOfSymbol(getResolvedSymbol(expr));
+ }
+ if (expr.kind === SyntaxKind.PropertyAccessExpression) {
+ const type = getDeclaredTypeOfReference((expr).expression);
+ return type && getTypeOfPropertyOfType(type, (expr).name.text);
+ }
+ return undefined;
+ }
+
+ function isDiscriminantProperty(type: Type, name: string) {
+ if (type && type.flags & TypeFlags.Union) {
+ let prop = getPropertyOfType(type, name);
+ if (!prop) {
+ // The type may be a union that includes nullable or primitive types. If filtering
+ // those out produces a different type, get the property from that type instead.
+ // Effectively, we're checking if this *could* be a discriminant property once nullable
+ // and primitive types are removed by other type guards.
+ const filteredType = getTypeWithFacts(type, TypeFacts.Discriminatable);
+ if (filteredType !== type && filteredType.flags & TypeFlags.Union) {
+ prop = getPropertyOfType(filteredType, name);
+ }
+ }
+ if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
+ if ((prop).isDiscriminantProperty === undefined) {
+ (prop).isDiscriminantProperty = !(prop).hasCommonType &&
+ isUnitUnionType(getTypeOfSymbol(prop));
+ }
+ return (prop).isDiscriminantProperty;
+ }
+ }
+ return false;
+ }
+
function isOrContainsMatchingReference(source: Node, target: Node) {
return isMatchingReference(source, target) || containsMatchingReference(source, target);
}
@@ -7585,29 +7911,60 @@ namespace ts {
// For example, when a variable of type number | string | boolean is assigned a value of type number | boolean,
// we remove type string.
function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) {
- if (declaredType !== assignedType && declaredType.flags & TypeFlags.Union) {
- const reducedTypes = filter(declaredType.types, t => typeMaybeAssignableTo(assignedType, t));
- if (reducedTypes.length) {
- return reducedTypes.length === 1 ? reducedTypes[0] : getUnionType(reducedTypes);
+ if (declaredType !== assignedType) {
+ const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t));
+ if (reducedType !== neverType) {
+ return reducedType;
}
}
return declaredType;
}
+ function getTypeFactsOfTypes(types: Type[]): TypeFacts {
+ let result: TypeFacts = TypeFacts.None;
+ for (const t of types) {
+ result |= getTypeFacts(t);
+ }
+ return result;
+ }
+
+ function isFunctionObjectType(type: ObjectType): boolean {
+ // We do a quick check for a "bind" property before performing the more expensive subtype
+ // check. This gives us a quicker out in the common case where an object type is not a function.
+ const resolved = resolveStructuredTypeMembers(type);
+ return !!(resolved.callSignatures.length || resolved.constructSignatures.length ||
+ resolved.members["bind"] && isTypeSubtypeOf(type, globalFunctionType));
+ }
+
function getTypeFacts(type: Type): TypeFacts {
const flags = type.flags;
- if (flags & TypeFlags.StringLike) {
+ if (flags & TypeFlags.String) {
return strictNullChecks ? TypeFacts.StringStrictFacts : TypeFacts.StringFacts;
}
- if (flags & TypeFlags.NumberLike) {
+ if (flags & TypeFlags.StringLiteral) {
+ return strictNullChecks ?
+ type === emptyStringType ? TypeFacts.EmptyStringStrictFacts : TypeFacts.NonEmptyStringStrictFacts :
+ type === emptyStringType ? TypeFacts.EmptyStringFacts : TypeFacts.NonEmptyStringFacts;
+ }
+ if (flags & (TypeFlags.Number | TypeFlags.Enum)) {
return strictNullChecks ? TypeFacts.NumberStrictFacts : TypeFacts.NumberFacts;
}
+ if (flags & (TypeFlags.NumberLiteral | TypeFlags.EnumLiteral)) {
+ const isZero = type === zeroType || type.flags & TypeFlags.EnumLiteral && (type).text === "0";
+ return strictNullChecks ?
+ isZero ? TypeFacts.ZeroStrictFacts : TypeFacts.NonZeroStrictFacts :
+ isZero ? TypeFacts.ZeroFacts : TypeFacts.NonZeroFacts;
+ }
if (flags & TypeFlags.Boolean) {
return strictNullChecks ? TypeFacts.BooleanStrictFacts : TypeFacts.BooleanFacts;
}
+ if (flags & TypeFlags.BooleanLike) {
+ return strictNullChecks ?
+ type === falseType ? TypeFacts.FalseStrictFacts : TypeFacts.TrueStrictFacts :
+ type === falseType ? TypeFacts.FalseFacts : TypeFacts.TrueFacts;
+ }
if (flags & TypeFlags.ObjectType) {
- const resolved = resolveStructuredTypeMembers(type);
- return resolved.callSignatures.length || resolved.constructSignatures.length || isTypeSubtypeOf(type, globalFunctionType) ?
+ return isFunctionObjectType(type) ?
strictNullChecks ? TypeFacts.FunctionStrictFacts : TypeFacts.FunctionFacts :
strictNullChecks ? TypeFacts.ObjectStrictFacts : TypeFacts.ObjectFacts;
}
@@ -7622,34 +7979,16 @@ namespace ts {
}
if (flags & TypeFlags.TypeParameter) {
const constraint = getConstraintOfTypeParameter(type);
- return constraint ? getTypeFacts(constraint) : TypeFacts.All;
+ return getTypeFacts(constraint || emptyObjectType);
}
- if (flags & TypeFlags.Intersection) {
- return reduceLeft((type).types, (flags, type) => flags |= getTypeFacts(type), TypeFacts.None);
+ if (flags & TypeFlags.UnionOrIntersection) {
+ return getTypeFactsOfTypes((type).types);
}
return TypeFacts.All;
}
function getTypeWithFacts(type: Type, include: TypeFacts) {
- if (!(type.flags & TypeFlags.Union)) {
- return getTypeFacts(type) & include ? type : neverType;
- }
- let firstType: Type;
- let types: Type[];
- for (const t of (type as UnionType).types) {
- if (getTypeFacts(t) & include) {
- if (!firstType) {
- firstType = t;
- }
- else {
- if (!types) {
- types = [firstType];
- }
- types.push(t);
- }
- }
- }
- return firstType ? types ? getUnionType(types, /*noSubtypeReduction*/ true) : firstType : neverType;
+ return filterType(type, t => (getTypeFacts(t) & include) !== 0);
}
function getTypeWithDefault(type: Type, defaultExpression: Expression) {
@@ -7761,16 +8100,22 @@ namespace ts {
getInitialTypeOfBindingElement(node);
}
- function getReferenceFromExpression(node: Expression): Expression {
+ function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression) {
+ return node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
+ getInitialType(node) :
+ getAssignedType(node);
+ }
+
+ function getReferenceCandidate(node: Expression): Expression {
switch (node.kind) {
case SyntaxKind.ParenthesizedExpression:
- return getReferenceFromExpression((node).expression);
+ return getReferenceCandidate((node).expression);
case SyntaxKind.BinaryExpression:
switch ((node).operatorToken.kind) {
case SyntaxKind.EqualsToken:
- return getReferenceFromExpression((node).left);
+ return getReferenceCandidate((node).left);
case SyntaxKind.CommaToken:
- return getReferenceFromExpression((node).right);
+ return getReferenceCandidate((node).right);
}
}
return node;
@@ -7778,10 +8123,10 @@ namespace ts {
function getTypeOfSwitchClause(clause: CaseClause | DefaultClause) {
if (clause.kind === SyntaxKind.CaseClause) {
- const expr = (clause).expression;
- return expr.kind === SyntaxKind.StringLiteral ? getStringLiteralTypeForText((expr).text) : checkExpression(expr);
+ const caseType = checkExpression((clause).expression);
+ return isUnitType(caseType) ? caseType : undefined;
}
- return undefined;
+ return neverType;
}
function getSwitchClauseTypes(switchStatement: SwitchStatement): Type[] {
@@ -7790,7 +8135,7 @@ namespace ts {
// If all case clauses specify expressions that have unit types, we return an array
// of those unit types. Otherwise we return an empty array.
const types = map(switchStatement.caseBlock.clauses, getTypeOfSwitchClause);
- links.switchTypes = forEach(types, t => !t || t.flags & TypeFlags.StringLiteral) ? types : emptyArray;
+ links.switchTypes = !contains(types, undefined) ? types : emptyArray;
}
return links.switchTypes;
}
@@ -7799,27 +8144,61 @@ namespace ts {
return source.flags & TypeFlags.Union ? !forEach((