diff --git a/src/project-manager.ts b/src/project-manager.ts
index a5ef62198..d25327d6e 100644
--- a/src/project-manager.ts
+++ b/src/project-manager.ts
@@ -74,6 +74,11 @@ export class ProjectManager implements Disposable {
 	 */
 	private ensuredModuleStructure?: Observable<never>;
 
+	/**
+	 * Observable that completes when extra dependencies pointed to by tsconfig.json have been loaded.
+	 */
+	private ensuredConfigDependencies?: Observable<never>;
+
 	/**
 	 * Observable that completes when `ensureAllFiles` completed
 	 */
@@ -252,6 +257,7 @@ export class ProjectManager implements Disposable {
 	 */
 	invalidateModuleStructure(): void {
 		this.ensuredModuleStructure = undefined;
+		this.ensuredConfigDependencies = undefined;
 		this.ensuredAllFiles = undefined;
 		this.ensuredOwnFiles = undefined;
 	}
@@ -321,6 +327,7 @@ export class ProjectManager implements Disposable {
 			span.addTags({ uri, maxDepth });
 			ignore.add(uri);
 			return this.ensureModuleStructure(span)
+				.concat(Observable.defer(() => this.ensureConfigDependencies()))
 				// If max depth was reached, don't go any further
 				.concat(Observable.defer(() => maxDepth === 0 ? Observable.empty<never>() : this.resolveReferencedFiles(uri)))
 				// Prevent cycles
@@ -337,6 +344,39 @@ export class ProjectManager implements Disposable {
 		});
 	}
 
+	/**
+	 * Determines if a tsconfig/jsconfig needs additional declaration files loaded.
+	 * @param filePath
+	 */
+	isConfigDependency(filePath: string): boolean {
+		for (const config of this.configurations()) {
+			config.ensureConfigFile();
+			if (config.isExpectedDeclarationFile(filePath)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * Loads files determined by tsconfig to be needed into the file system
+	 */
+	ensureConfigDependencies(childOf = new Span()): Observable<never> {
+		return traceObservable('Ensure config dependencies', childOf, span => {
+			if (!this.ensuredConfigDependencies) {
+				this.ensuredConfigDependencies = observableFromIterable(this.inMemoryFs.uris())
+				.filter(uri => this.isConfigDependency(uri2path(uri)))
+				.mergeMap(uri => this.updater.ensure(uri))
+				.do(noop, err => {
+					this.ensuredConfigDependencies = undefined;
+				})
+				.publishReplay()
+				.refCount() as Observable<never>;
+			}
+			return this.ensuredConfigDependencies;
+		});
+	}
+
 	/**
 	 * Invalidates a cache entry for `resolveReferencedFiles` (e.g. because the file changed)
 	 *
@@ -734,6 +774,11 @@ export class ProjectConfiguration {
 	 */
 	private expectedFilePaths = new Set<string>();
 
+	/**
+	 * List of resolved extra root directories to allow global type declaration files to be loaded from.
+	 */
+	private typeRoots: string[];
+
 	/**
 	 * @param fs file system to use
 	 * @param documentRegistry Shared DocumentRegistry that manages SourceFile objects
@@ -838,6 +883,11 @@ export class ProjectConfiguration {
 		this.expectedFilePaths = new Set(configParseResult.fileNames);
 
 		const options = configParseResult.options;
+		const pathResolver = /^[a-z]:\//i.test(base) ? path.win32 : path.posix;
+		this.typeRoots = options.typeRoots ?
+			options.typeRoots.map((r: string) => pathResolver.resolve(this.rootFilePath, r)) :
+			[];
+
 		if (/(^|\/)jsconfig\.json$/.test(this.configFilePath)) {
 			options.allowJs = true;
 		}
@@ -864,6 +914,16 @@ export class ProjectConfiguration {
 
 	private ensuredBasicFiles = false;
 
+	/**
+	 * Determines if a fileName is a declaration file within expected files or type roots
+	 * @param fileName
+	 */
+	public isExpectedDeclarationFile(fileName: string) {
+		return isDeclarationFile(fileName) &&
+				(this.expectedFilePaths.has(toUnixPath(fileName)) ||
+				this.typeRoots.some(root => fileName.startsWith(root)));
+	}
+
 	/**
 	 * Ensures we added basic files (global TS files, dependencies, declarations)
 	 */
@@ -882,7 +942,8 @@ export class ProjectConfiguration {
 		// Add all global declaration files from the workspace and all declarations from the project
 		for (const uri of this.fs.uris()) {
 			const fileName = uri2path(uri);
-			if (isGlobalTSFile(fileName) || (isDeclarationFile(fileName) && this.expectedFilePaths.has(toUnixPath(fileName)))) {
+			if (isGlobalTSFile(fileName) ||
+				this.isExpectedDeclarationFile(fileName)) {
 				const sourceFile = program.getSourceFile(fileName);
 				if (!sourceFile) {
 					this.getHost().addFile(fileName);
diff --git a/src/test/project-manager.test.ts b/src/test/project-manager.test.ts
index ee40fabfe..aa8e8392b 100644
--- a/src/test/project-manager.test.ts
+++ b/src/test/project-manager.test.ts
@@ -24,6 +24,26 @@ describe('ProjectManager', () => {
 		assert.isDefined(configs.find(config => config.configFilePath === '/foo/tsconfig.json'));
 	});
 
+	describe('ensureBasicFiles', () => {
+		beforeEach(async () => {
+			memfs = new InMemoryFileSystem('/');
+			const localfs = new MapFileSystem(new Map([
+				['file:///project/package.json', '{"name": "package-name-1"}'],
+				['file:///project/tsconfig.json', '{ "compilerOptions": { "typeRoots": ["../types"]} }'],
+				['file:///project/file.ts', 'console.log(GLOBALCONSTANT);'],
+				['file:///types/types.d.ts', 'declare var GLOBALCONSTANT=1;']
+
+			]));
+			const updater = new FileSystemUpdater(localfs, memfs);
+			projectManager = new ProjectManager('/', memfs, updater, true);
+		});
+		it('loads files from typeRoots', async () => {
+			await projectManager.ensureReferencedFiles('file:///project/file.ts').toPromise();
+			memfs.getContent('file:///project/file.ts');
+			memfs.getContent('file:///types/types.d.ts');
+		});
+	});
+
 	describe('getPackageName()', () => {
 		beforeEach(async () => {
 			memfs = new InMemoryFileSystem('/');