Search code examples
typescriptactivexobject

Combined class and interface type


In Javascript (under WScript or HTA), I would write the following pattern:

var fso = new ActiveXObject('Scripting.FileSystemObject');
var ext = fso.GetExtensionName('C:\test.txt');    //returns 'txt'
var badResult = fso.NonExistentMethod();          //raises a runtime error

Using TypeScript, I can declare an interface which will prevent this sort of error:

module Scripting {
    export interface FileSystemObject {
        GetExtensionName(path: string): string,
        GetParentFolderName(path: string): string
    }
}

var fso: Scripting.FileSystemObject = new ActiveXObject('Scripting.FileSystemObject');
var ext = fso.GetExtensionName('C:\test.txt'); //ext is recognized as a string
//var badResult = fso.NonExistentMethod();     //will not compile

However, I would like to initialize the fso variable like this:

var fso = new Scripting.FileSystemObject();

and have the type system automatically deduce that the type of fso is the interface Scripting.FileSystemObject.

Can this be done?


1) I can't add the constructor to the interface, because the interface is only the shape of the object, not its implementation.


2) I thought about creating an internal interface, and extending the interface with a class and a constructor:

module Scripting {
    interface Internal {
        GetExtensionName(path: string): string;
        GetParentFolderName(path: string): string;
    }

    export class FileSystemObject extends Internal {
        constructor() {
            return new ActiveXObject('Scripting.FileSystemObject');
        }
    }
}

but constructors cannot be added to an extending class without a call to the base type constructor, which an interface does not have.


3) I cannot have a single declared class, because the constructor is still an implementation and cannot be written into a declared class, which is an ambient context.


Solution

  • deduce that the type of fso is the interface Scripting.FileSystemObject.Can this be done?

    Sure. Just declare to have something which when called with new will return the correct type. i.e. export var FileSystemObject: { new (): FileSystemObject }; below :

    declare module Scripting {
        export interface FileSystemObject {
            GetExtensionName(path: string): string;
            GetParentFolderName(path: string): string;
        }
        export var FileSystemObject: { new (): FileSystemObject };
    }
    
    var fso: Scripting.FileSystemObject = new Scripting.FileSystemObject();
    var ext = fso.GetExtensionName('C:\test.txt'); //ext is recognized as a string
    var badResult = fso.NonExistentMethod();     //will not compile
    

    try it

    UPDATE To implement something like this yourself you can do:

    module Scripting {
        export interface FileSystemObject {
            GetExtensionName(path: string): string;
            GetParentFolderName(path: string): string;
        }
        export var FileSystemObject: { new (): FileSystemObject } = <any>function(){
            return new ActiveXObject('Scripting.FileSystemObject');
        };
    }
    
    var fso: Scripting.FileSystemObject = new Scripting.FileSystemObject();
    var ext = fso.GetExtensionName('C:\test.txt'); //ext is recognized as a string
    var badResult = fso.NonExistentMethod();     //will not compile