Ver código fonte

Merge pull request #2675 from blink1073/strict-null-continued

Strict null checks in Document Manager and File Browser
Afshin Darian 7 anos atrás
pai
commit
d9371535b8

+ 1 - 9
packages/docmanager/src/dialogs.ts

@@ -80,7 +80,7 @@ function renameFile(manager: IDocumentManager, oldPath: string, newPath: string)
     };
     return showDialog(options).then(button => {
       if (!button.accept) {
-        return null;
+        return Promise.resolve(null);
       }
       return manager.overwrite(oldPath, newPath);
     });
@@ -122,14 +122,6 @@ class RenameHandler extends Widget {
     this.inputNode.setSelectionRange(0, value.length - ext.length);
   }
 
-  /**
-   * Dispose of the resources used by the widget.
-   */
-  dispose(): void {
-    this._manager = null;
-    super.dispose();
-  }
-
   /**
    * Get the input text node.
    */

+ 31 - 31
packages/docmanager/src/manager.ts

@@ -87,7 +87,7 @@ class DocumentManager implements IDisposable {
     this.services = options.manager;
 
     this._opener = options.opener;
-    this._modelDBFactory = options.modelDBFactory;
+    this._modelDBFactory = options.modelDBFactory || null;
 
     let widgetManager = new DocumentWidgetManager({ registry: this.registry });
     widgetManager.activateRequested.connect(this._onActivateRequested, this);
@@ -115,24 +115,22 @@ class DocumentManager implements IDisposable {
    * Get whether the document manager has been disposed.
    */
   get isDisposed(): boolean {
-    return this._widgetManager === null;
+    return this._isDisposed;
   }
 
   /**
    * Dispose of the resources held by the document manager.
    */
   dispose(): void {
-    if (this._widgetManager === null) {
+    if (this.isDisposed) {
       return;
     }
-
-    let widgetManager = this._widgetManager;
-    this._widgetManager = null;
+    this._isDisposed = true;
     Signal.clearData(this);
     each(toArray(this._contexts), context => {
-      widgetManager.closeWidgets(context);
+      this._widgetManager.closeWidgets(context);
     });
-    widgetManager.dispose();
+    this._widgetManager.dispose();
     this._contexts.length = 0;
   }
 
@@ -147,7 +145,7 @@ class DocumentManager implements IDisposable {
    *  Uses the same widget factory and context as the source, or returns
    *  `undefined` if the source widget is not managed by this manager.
    */
-  cloneWidget(widget: Widget): Widget {
+  cloneWidget(widget: Widget): Widget | undefined {
     return this._widgetManager.cloneWidget(widget);
   }
 
@@ -182,7 +180,7 @@ class DocumentManager implements IDisposable {
    *
    * @returns The context associated with the widget, or `undefined`.
    */
-  contextForWidget(widget: Widget): DocumentRegistry.Context {
+  contextForWidget(widget: Widget): DocumentRegistry.Context | undefined {
     return this._widgetManager.contextForWidget(widget);
   }
 
@@ -248,12 +246,12 @@ class DocumentManager implements IDisposable {
    * This can be used to use an existing widget instead of opening
    * a new widget.
    */
-  findWidget(path: string, widgetName='default'): Widget {
+  findWidget(path: string, widgetName='default'): Widget | undefined {
     if (widgetName === 'default') {
       let extname = DocumentRegistry.extname(path);
       let factory = this.registry.defaultWidgetFactory(extname);
       if (!factory) {
-        return;
+        return undefined;
       }
       widgetName = factory.name;
     }
@@ -261,6 +259,7 @@ class DocumentManager implements IDisposable {
     if (context) {
       return this._widgetManager.findWidget(context, widgetName);
     }
+    return undefined;
   }
 
   /**
@@ -290,7 +289,7 @@ class DocumentManager implements IDisposable {
    * This function will return `undefined` if a valid widget factory
    * cannot be found.
    */
-  open(path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget {
+  open(path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget | undefined {
     return this._createOrOpenDocument('open', path, widgetName, kernel);
   }
 
@@ -310,7 +309,7 @@ class DocumentManager implements IDisposable {
    * This function will return `undefined` if a valid widget factory
    * cannot be found.
    */
-  openOrReveal(path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget {
+  openOrReveal(path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget | undefined {
     let widget = this.findWidget(path, widgetName);
     if (widget) {
       this._opener.open(widget);
@@ -356,7 +355,7 @@ class DocumentManager implements IDisposable {
   /**
    * Find a context for a given path and factory name.
    */
-  private _findContext(path: string, factoryName: string): Private.IContext {
+  private _findContext(path: string, factoryName: string): Private.IContext | undefined {
     return find(this._contexts, context => {
       return context.factoryName === factoryName && context.path === path;
     });
@@ -365,7 +364,7 @@ class DocumentManager implements IDisposable {
   /**
    * Get a context for a given path.
    */
-  private _contextForPath(path: string): Private.IContext {
+  private _contextForPath(path: string): Private.IContext | undefined {
     return find(this._contexts, context => context.path === path);
   }
 
@@ -377,7 +376,7 @@ class DocumentManager implements IDisposable {
       this._widgetManager.adoptWidget(context, widget);
       this._opener.open(widget);
     };
-    let modelDBFactory = this.services.contents.getModelDBFactory(path) || null;
+    let modelDBFactory = this.services.contents.getModelDBFactory(path) || undefined;
     let context = new Context({
       opener: adopter,
       manager: this.services,
@@ -409,13 +408,13 @@ class DocumentManager implements IDisposable {
   /**
    * Get the widget factory for a given widget name.
    */
-  private _widgetFactoryFor(path: string, widgetName: string): DocumentRegistry.WidgetFactory {
+  private _widgetFactoryFor(path: string, widgetName: string): DocumentRegistry.WidgetFactory | undefined {
     let { registry } = this;
     if (widgetName === 'default') {
       let extname = DocumentRegistry.extname(path);
       let factory = registry.defaultWidgetFactory(extname);
       if (!factory) {
-        return;
+        return undefined;
       }
       widgetName = factory.name;
     }
@@ -430,14 +429,14 @@ class DocumentManager implements IDisposable {
    * The two cases differ in how the document context is handled, but the creation
    * of the widget and launching of the kernel are identical.
    */
-  private _createOrOpenDocument(which: 'open'|'create', path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget {
+  private _createOrOpenDocument(which: 'open'|'create', path: string, widgetName='default', kernel?: Partial<Kernel.IModel>): Widget | undefined {
     let widgetFactory = this._widgetFactoryFor(path, widgetName);
     if (!widgetFactory) {
-      return;
+      return undefined;
     }
-    let factory = this.registry.getModelFactory(widgetFactory.modelName);
+    let factory = this.registry.getModelFactory(widgetFactory.modelName || 'text');
     if (!factory) {
-      return;
+      return undefined;
     }
 
     // Handle the kernel pereference.
@@ -446,12 +445,12 @@ class DocumentManager implements IDisposable {
       ext, widgetFactory.name, kernel
     );
 
-    let context: Private.IContext = null;
+    let context: Private.IContext | null = null;
 
     // Handle the load-from-disk case
     if (which === 'open') {
       // Use an existing context if available.
-      context = this._findContext(path, factory.name);
+      context = this._findContext(path, factory.name) || null;
       if (!context) {
         context = this._createContext(path, factory, preference);
         // Populate the model, either from disk or a
@@ -464,7 +463,7 @@ class DocumentManager implements IDisposable {
       context.save();
     }
 
-    let widget = this._widgetManager.createWidget(widgetFactory, context);
+    let widget = this._widgetManager.createWidget(widgetFactory, context!);
     this._opener.open(widget);
     return widget;
   }
@@ -478,9 +477,10 @@ class DocumentManager implements IDisposable {
 
   private _activateRequested = new Signal<this, string>(this);
   private _contexts: Private.IContext[] = [];
-  private _modelDBFactory: ModelDB.IFactory = null;
-  private _opener: DocumentManager.IWidgetOpener = null;
-  private _widgetManager: DocumentWidgetManager = null;
+  private _modelDBFactory: ModelDB.IFactory | null = null;
+  private _opener: DocumentManager.IWidgetOpener;
+  private _widgetManager: DocumentWidgetManager;
+  private _isDisposed = false;
 }
 
 
@@ -536,9 +536,9 @@ namespace Private {
    * An attached property for a context save handler.
    */
   export
-  const saveHandlerProperty = new AttachedProperty<DocumentRegistry.Context, SaveHandler>({
+  const saveHandlerProperty = new AttachedProperty<DocumentRegistry.Context, SaveHandler | undefined>({
     name: 'saveHandler',
-    create: () => null
+    create: () => undefined
   });
 
   /**

+ 8 - 6
packages/docmanager/src/savehandler.ts

@@ -36,7 +36,8 @@ class SaveHandler implements IDisposable {
   constructor(options: SaveHandler.IOptions) {
     this._manager = options.manager;
     this._context = options.context;
-    this._minInterval = options.saveInterval * 1000 || 120000;
+    let interval = options.saveInterval || 120;
+    this._minInterval = interval * 1000;
     this._interval = this._minInterval;
     this._warnOnConflict = !this._context.model.modelDB.isCollaborative;
     // Restart the timer when the contents model is updated.
@@ -68,17 +69,17 @@ class SaveHandler implements IDisposable {
    * Get whether the save handler is disposed.
    */
   get isDisposed(): boolean {
-    return this._context === null;
+    return this._isDisposed;
   }
 
   /**
    * Dispose of the resources used by the save handler.
    */
   dispose(): void {
-    if (this._context === null) {
+    if (this.isDisposed) {
       return;
     }
-    this._context = null;
+    this._isDisposed = true;
     clearTimeout(this._autosaveTimer);
     Signal.clearData(this);
   }
@@ -199,10 +200,11 @@ class SaveHandler implements IDisposable {
   private _minInterval = -1;
   private _interval = -1;
   private _warnOnConflict = true;
-  private _context: DocumentRegistry.Context = null;
-  private _manager: ServiceManager.IManager = null;
+  private _context: DocumentRegistry.Context;
+  private _manager: ServiceManager.IManager;
   private _isActive = false;
   private _inDialog = false;
+  private _isDisposed = false;
 }
 
 

+ 53 - 21
packages/docmanager/src/widgetmanager.ts

@@ -37,6 +37,10 @@ import {
   DocumentRegistry
 } from '@jupyterlab/docregistry';
 
+import {
+  Contents
+} from '@jupyterlab/services';
+
 
 /**
  * The class name added to document widgets.
@@ -67,17 +71,17 @@ class DocumentWidgetManager implements IDisposable {
    * Test whether the document widget manager is disposed.
    */
   get isDisposed(): boolean {
-    return this._registry === null;
+    return this._isDisposed;
   }
 
   /**
    * Dispose of the resources used by the widget manager.
    */
   dispose(): void {
-    if (this._registry == null) {
+    if (this.isDisposed) {
       return;
     }
-    this._registry = null;
+    this._isDisposed = true;
     Signal.disconnectReceiver(this);
   }
 
@@ -142,13 +146,17 @@ class DocumentWidgetManager implements IDisposable {
    * This can be used to use an existing widget instead of opening
    * a new widget.
    */
-  findWidget(context: DocumentRegistry.Context, widgetName: string): Widget {
+  findWidget(context: DocumentRegistry.Context, widgetName: string): Widget | undefined {
     let widgets = Private.widgetsProperty.get(context);
+    if (!widgets) {
+      return undefined;
+    }
     return find(widgets, widget => {
-      let name = Private.factoryProperty.get(widget).name;
-      if (name === widgetName) {
-        return true;
+      let factory = Private.factoryProperty.get(widget);
+      if (!factory) {
+        return false;
       }
+      return factory.name === widgetName;
     });
   }
 
@@ -159,8 +167,8 @@ class DocumentWidgetManager implements IDisposable {
    *
    * @returns The context associated with the widget, or `undefined`.
    */
-  contextForWidget(widget: Widget): DocumentRegistry.Context {
-    return !widget ? void 0 : Private.contextProperty.get(widget);
+  contextForWidget(widget: Widget): DocumentRegistry.Context | undefined {
+    return Private.contextProperty.get(widget);
   }
 
   /**
@@ -171,15 +179,18 @@ class DocumentWidgetManager implements IDisposable {
    * @returns A new widget or `undefined`.
    *
    * #### Notes
-   *  Uses the same widget factory and context as the source, or returns
-   *  `undefined` if the source widget is not managed by this manager.
+   *  Uses the same widget factory and context as the source, or throws
+   *  if the source widget is not managed by this manager.
    */
-  cloneWidget(widget: Widget): Widget {
+  cloneWidget(widget: Widget): Widget | undefined {
     let context = Private.contextProperty.get(widget);
     if (!context) {
-      return;
+      return undefined;
     }
     let factory = Private.factoryProperty.get(widget);
+    if (!factory) {
+      return undefined;
+    }
     let newWidget = this.createWidget(factory, context);
     this.adoptWidget(context, newWidget);
     return widget;
@@ -214,7 +225,9 @@ class DocumentWidgetManager implements IDisposable {
       return false;
     case 'activate-request':
       let context = this.contextForWidget(handler as Widget);
-      this._activateRequested.emit(context.path);
+      if (context) {
+        this._activateRequested.emit(context.path);
+      }
       break;
     default:
       break;
@@ -229,12 +242,15 @@ class DocumentWidgetManager implements IDisposable {
    */
   protected setCaption(widget: Widget): void {
     let context = Private.contextProperty.get(widget);
+    if (!context) {
+      return;
+    }
     let model = context.contentsModel;
     if (!model) {
       widget.title.caption = '';
       return;
     }
-    context.listCheckpoints().then(checkpoints => {
+    context.listCheckpoints().then((checkpoints: Contents.ICheckpointModel[]) => {
       if (widget.isDisposed) {
         return;
       }
@@ -282,12 +298,21 @@ class DocumentWidgetManager implements IDisposable {
       return Promise.resolve(true);
     }
     let widgets = Private.widgetsProperty.get(context);
+    if (!widgets) {
+      return Promise.resolve(true);
+    }
     // Filter by whether the factories are read only.
     widgets = toArray(filter(widgets, widget => {
       let factory = Private.factoryProperty.get(widget);
+      if (!factory) {
+        return false;
+      }
       return factory.readOnly === false;
     }));
     let factory = Private.factoryProperty.get(widget);
+    if (!factory) {
+      return Promise.resolve(true);
+    }
     let model = context.model;
     if (!model.dirty || widgets.length > 1 || factory.readOnly) {
       return Promise.resolve(true);
@@ -307,8 +332,14 @@ class DocumentWidgetManager implements IDisposable {
    */
   private _widgetDisposed(widget: Widget): void {
     let context = Private.contextProperty.get(widget);
+    if (!context) {
+      return;
+    }
     let widgets = Private.widgetsProperty.get(context);
-     // Remove the widget.
+    if (!widgets) {
+      return;
+    }
+    // Remove the widget.
     ArrayExt.removeFirstOf(widgets, widget);
     // Dispose of the context if this is the last widget using it.
     if (!widgets.length) {
@@ -340,8 +371,9 @@ class DocumentWidgetManager implements IDisposable {
     each(widgets, widget => { this.setCaption(widget); });
   }
 
-  private _registry: DocumentRegistry = null;
+  private _registry: DocumentRegistry;
   private _activateRequested = new Signal<this, string>(this);
+  private _isDisposed = false;
 }
 
 
@@ -371,18 +403,18 @@ namespace Private {
    * A private attached property for a widget context.
    */
   export
-  const contextProperty = new AttachedProperty<Widget, DocumentRegistry.Context>({
+  const contextProperty = new AttachedProperty<Widget, DocumentRegistry.Context | undefined>({
     name: 'context',
-    create: () => null
+    create: () => undefined
   });
 
   /**
    * A private attached property for a widget factory.
    */
   export
-  const factoryProperty = new AttachedProperty<Widget, DocumentRegistry.WidgetFactory> ({
+  const factoryProperty = new AttachedProperty<Widget, DocumentRegistry.WidgetFactory | undefined> ({
     name: 'factory',
-    create: () => null
+    create: () => undefined
   });
 
   /**

+ 4 - 16
packages/filebrowser/src/browser.ts

@@ -157,17 +157,6 @@ class FileBrowser extends Widget {
    */
   readonly toolbar: Toolbar<Widget>;
 
-  /**
-   * Dispose of the resources held by the file browser.
-   */
-  dispose() {
-    this._crumbs = null;
-    this._listing = null;
-    this._manager = null;
-    this._model = null;
-    super.dispose();
-  }
-
   /**
    * Create an iterator over the listing's selected items.
    *
@@ -264,7 +253,7 @@ class FileBrowser extends Widget {
    *
    * @returns The path to the selected file.
    */
-  pathForClick(event: MouseEvent): string {
+  pathForClick(event: MouseEvent): string | undefined {
     return this._listing.pathForClick(event);
   }
 
@@ -281,10 +270,9 @@ class FileBrowser extends Widget {
     });
   }
 
-  private _crumbs: BreadCrumbs | null = null;
-  private _listing: DirListing | null = null;
-  private _manager: DocumentManager | null = null;
-  private _model: FileBrowserModel | null = null;
+  private _crumbs: BreadCrumbs;
+  private _listing: DirListing;
+  private _manager: DocumentManager;
   private _showingError = false;
 }
 

+ 11 - 9
packages/filebrowser/src/crumbs.ts

@@ -169,7 +169,7 @@ class BreadCrumbs extends Widget {
   /**
    * Handle the `'click'` event for the widget.
    */
-  private _evtClick(event: MouseEvent) {
+  private _evtClick(event: MouseEvent): void {
     // Do nothing if it's not a left mouse press.
     if (event.button !== 0) {
       return;
@@ -189,7 +189,7 @@ class BreadCrumbs extends Widget {
         event.stopPropagation();
         return;
       }
-      node = node.parentElement;
+      node = node.parentElement as HTMLElement;
     }
   }
 
@@ -294,6 +294,7 @@ class BreadCrumbs extends Widget {
                 if (!model.isDisposed) {
                   return model.manager.rename(oldPath, newPath);
                 }
+                return Promise.reject('Model is disposed') as Promise<any>;
               });
             }
           });
@@ -305,9 +306,9 @@ class BreadCrumbs extends Widget {
     });
   }
 
-  private _model: FileBrowserModel = null;
-  private _crumbs: ReadonlyArray<HTMLElement> = null;
-  private _crumbSeps: ReadonlyArray<HTMLElement> = null;
+  private _model: FileBrowserModel;
+  private _crumbs: ReadonlyArray<HTMLElement>;
+  private _crumbSeps: ReadonlyArray<HTMLElement>;
 }
 
 
@@ -351,14 +352,15 @@ namespace Private {
    */
   export
   function updateCrumbs(breadcrumbs: ReadonlyArray<HTMLElement>, separators: ReadonlyArray<HTMLElement>, path: string) {
-    let node = breadcrumbs[0].parentNode;
+    let node = breadcrumbs[0].parentNode as HTMLElement;
 
     // Remove all but the home node.
-    while (node.firstChild.nextSibling) {
-      node.removeChild(node.firstChild.nextSibling);
+    let firstChild = node.firstChild as HTMLElement;
+    while (firstChild && firstChild.nextSibling) {
+      node.removeChild(firstChild.nextSibling);
     }
 
-    let localPath = path.split(':').pop();
+    let localPath = path.split(':').pop() as string;
     let localParts = localPath.split('/');
     let parts = path.split('/');
     if (parts.length > 2) {

+ 33 - 23
packages/filebrowser/src/listing.ts

@@ -277,12 +277,9 @@ class DirListing extends Widget {
    * Dispose of the resources held by the directory listing.
    */
   dispose(): void {
-    this._model = null;
-    this._items = null;
-    this._editNode = null;
-    this._drag = null;
-    this._dragData = null;
-    this._manager = null;
+    this._items.length = 0;
+    this._sortedItems.length = 0;
+    this._clipboard.length = 0;
     super.dispose();
   }
 
@@ -606,12 +603,13 @@ class DirListing extends Widget {
    *
    * @returns The path to the selected file.
    */
-  pathForClick(event: MouseEvent): string {
+  pathForClick(event: MouseEvent): string | undefined {
     let items = this._sortedItems;
     let index = Private.hitTestNodes(this._items, event.clientX, event.clientY);
     if (index !== -1) {
       return items[index].path;
     }
+    return undefined;
   }
 
   /**
@@ -747,7 +745,7 @@ class DirListing extends Widget {
     // Remove any excess item nodes.
     while (nodes.length > items.length) {
       let node = nodes.pop();
-      content.removeChild(node);
+      content.removeChild(node!);
     }
 
     // Add any missing item nodes.
@@ -914,7 +912,7 @@ class DirListing extends Widget {
     event.stopPropagation();
 
     // Bail if we are the one dragging.
-    if (this._drag) {
+    if (this._drag || !this._dragData) {
       return;
     }
 
@@ -1120,7 +1118,7 @@ class DirListing extends Widget {
     const manager = this._manager;
 
     // Handle the items.
-    const promises: Promise<Contents.IModel>[] = [];
+    const promises: Promise<Contents.IModel | null>[] = [];
     const paths = event.mimeData.getData(CONTENTS_MIME) as string[];
     for (let path of paths) {
       let name = PathExt.basename(path);
@@ -1143,7 +1141,7 @@ class DirListing extends Widget {
     let selectedNames = Object.keys(this._selection);
     let source = this._items[index];
     let items = this._sortedItems;
-    let item: Contents.IModel = null;
+    let item: Contents.IModel | undefined;
 
     // If the source node is not selected, use just that node.
     if (!source.classList.contains(SELECTED_CLASS)) {
@@ -1154,6 +1152,10 @@ class DirListing extends Widget {
       item = find(items, value => value.name === name);
     }
 
+    if (!item) {
+      return;
+    }
+
     // Create the drag image.
     let dragImage = this.renderer.createDragImage(source, selectedNames.length, item);
 
@@ -1171,6 +1173,9 @@ class DirListing extends Widget {
     this._drag.mimeData.setData(CONTENTS_MIME, paths);
     if (item && item.type !== 'directory') {
       this._drag.mimeData.setData(FACTORY_MIME, () => {
+        if (!item) {
+          return;
+        }
         let path = item.path;
         let widget = this._manager.findWidget(path);
         if (!widget) {
@@ -1334,7 +1339,7 @@ class DirListing extends Widget {
       }
       if (this.isDisposed) {
         this._inRename = false;
-        return;
+        return Promise.reject('Disposed') as Promise<string>;
       }
 
       const manager = this._manager;
@@ -1348,7 +1353,7 @@ class DirListing extends Widget {
       }).then(() => {
         if (this.isDisposed) {
           this._inRename = false;
-          return;
+          return Promise.reject('Disposed') as Promise<string>;
         }
         this.selectItemByName(newName);
         this._inRename = false;
@@ -1402,9 +1407,14 @@ class DirListing extends Widget {
    * Handle a `fileChanged` signal from the model.
    */
   private _onFileChanged(sender: FileBrowserModel, args: Contents.IChangedArgs) {
-    if (args.type === 'new') {
-      this.selectItemByName(args.newValue.name).then(() => {
-        if (!this.isDisposed && args.newValue === 'directory') {
+    let newValue = args.newValue;
+    if (!newValue) {
+      return;
+    }
+    let name = args.newValue.name;
+    if (args.type === 'new' && name) {
+      this.selectItemByName(name).then(() => {
+        if (!this.isDisposed && newValue.type === 'directory') {
           this._doRename();
         }
       });
@@ -1423,23 +1433,23 @@ class DirListing extends Widget {
     this.selectItemByName(basename);
   }
 
-  private _model: FileBrowserModel = null;
-  private _editNode: HTMLInputElement = null;
+  private _model: FileBrowserModel;
+  private _editNode: HTMLInputElement;
   private _items: HTMLElement[] = [];
   private _sortedItems: Contents.IModel[] = [];
   private _sortState: DirListing.ISortState = { direction: 'ascending', key: 'name' };
-  private _drag: Drag = null;
-  private _dragData: { pressX: number, pressY: number, index: number } = null;
+  private _drag: Drag | null = null;
+  private _dragData: { pressX: number, pressY: number, index: number } | null = null;
   private _selectTimer = -1;
   private _noSelectTimer = -1;
   private _isCut = false;
   private _prevPath = '';
   private _clipboard: string[] = [];
-  private _manager: IDocumentManager = null;
+  private _manager: IDocumentManager;
   private _softSelection = '';
   private _inContext = false;
   private _selection: { [key: string]: boolean; } = Object.create(null);
-  private _renderer: DirListing.IRenderer = null;
+  private _renderer: DirListing.IRenderer;
   private _searchPrefix: string = '';
   private _searchPrefixTimer = -1;
   private _inRename = false;
@@ -1632,7 +1642,7 @@ namespace DirListing {
         name.classList.remove(DESCENDING_CLASS);
         return state;
       }
-      return void 0;
+      return state;
     }
 
     /**

+ 8 - 7
packages/filebrowser/src/model.ts

@@ -125,17 +125,17 @@ class FileBrowserModel implements IDisposable {
    * Get whether the model is disposed.
    */
   get isDisposed(): boolean {
-    return this._model === null;
+    return this._isDisposed;
   }
 
   /**
    * Dispose of the resources held by the model.
    */
   dispose(): void {
-    if (this._model === null) {
+    if (this.isDisposed) {
       return;
     }
-    this._model = null;
+    this._isDisposed = true;
     clearTimeout(this._timeoutId);
     this._sessions.length = 0;
     this._items.length = 0;
@@ -183,7 +183,7 @@ class FileBrowserModel implements IDisposable {
       newValue = this._pendingPath || this._model.path;
     }
     // Collapse requests to the same directory.
-    if (newValue === this._pendingPath) {
+    if (newValue === this._pendingPath && this._pending) {
       return this._pending;
     }
     let oldValue = this.path;
@@ -308,7 +308,7 @@ class FileBrowserModel implements IDisposable {
       throw new Error(msg);
     }, () => {
       if (this.isDisposed) {
-        return;
+        return Promise.reject('Disposed') as Promise<Contents.IModel>;
       }
       return this._upload(file);
     });
@@ -446,8 +446,8 @@ class FileBrowserModel implements IDisposable {
   private _model: Contents.IModel;
   private _pathChanged = new Signal<this, IChangedArgs<string>>(this);
   private _paths = new Set<string>();
-  private _pending: Promise<void> = null;
-  private _pendingPath: string = null;
+  private _pending: Promise<void> | null = null;
+  private _pendingPath: string | null = null;
   private _refreshed = new Signal<this, void>(this);
   private _lastRefresh = -1;
   private _requested = false;
@@ -455,6 +455,7 @@ class FileBrowserModel implements IDisposable {
   private _state: IStateDB | null = null;
   private _timeoutId = -1;
   private _driveName: string;
+  private _isDisposed = false;
 }
 
 

+ 3 - 3
packages/filebrowser/src/upload.ts

@@ -87,8 +87,8 @@ class Uploader extends ToolbarButton {
    */
   private _onInputClicked(): void {
     // In order to allow repeated uploads of the same file (with delete in between),
-    // we need to null out the input value to trigger a change event.
-    this._input.value = null;
+    // we need to clear the input value to trigger a change event.
+    this._input.value = '';
   }
 
   /**
@@ -116,7 +116,7 @@ class Uploader extends ToolbarButton {
     };
     return showDialog(options).then(button => {
       if (this.isDisposed || button.accept) {
-        return;
+        return Promise.resolve(void 0);
       }
       return this.model.upload(file, true);
     });

+ 1 - 1
test/src/docmanager/manager.spec.ts

@@ -277,7 +277,7 @@ describe('@jupyterlab/docmanager', () => {
 
       it('should fail to find the context for the widget', () => {
         widget = new Widget();
-        expect(manager.contextForWidget(widget)).to.be(null);
+        expect(manager.contextForWidget(widget)).to.be(undefined);
       });
 
     });

+ 2 - 2
test/src/docmanager/widgetmanager.spec.ts

@@ -201,8 +201,8 @@ describe('@jupyterlab/docmanager', () => {
         expect(manager.contextForWidget(widget)).to.be(context);
       });
 
-      it('should return null if not tracked', () => {
-        expect(manager.contextForWidget(new Widget())).to.be(null);
+      it('should return undefined if not tracked', () => {
+        expect(manager.contextForWidget(new Widget())).to.be(undefined);
       });
 
     });