VSTS extensions, mixing VSS.require and import ... require

I have been starting to write more and more typescript lately and quite enjoying it. I have also been writing a few vsts extensions and using typescript to write vsts extensions seems to be working quite nicely. One of the best things, for me, about typescript is to use type information which gives compile time verification and intellisense.

This is good but when it comes to VSTS extensions, almost every sample I have seen follows this pattern:

VSS.require(['ReleaseManagement/Core/RestClient'], (RmClientService) => { //do something with RmClientService }

What happens here is that vsts provides the RmClientService, you then do something with it. The RmClientService is (forgive my lack of typescript here) a pointer to the module that is defined as ‘ReleaseManagement/Core/RestClient’ in the vss web SDK.

VSS.require has to be used. If you just did a require(‘ReleaseManagement/Core/RestClient’) then the reference you get back won’t have been initialized in vsts.

VSS.require doesn’t actually import a module anyway there is no way (that I know of) to add type information to RmClientService.

If you find the RmClientService definition it looks like:

`
declare module “ReleaseManagement/Core/RestClient” {
import Contracts = require(“ReleaseManagement/Core/Contracts”);
import VSS_FormInput_Contracts = require(“VSS/Common/Contracts/FormInput”);
import VSS_WebApi = require(“VSS/WebApi/RestClient”);

/**

  • Gets an http client targeting the latest released version of the APIs.
  • @return ReleaseHttpClient4
    */
    export function getClient(options?: VSS_WebApi.IVssHttpClientOptions): ReleaseHttpClient4;
    }
    `

Because this is a module and not a type we can’t declare the type on RmClientService but we can define the type that is returned by getClient().

So, in our code, once we call getClient we know the type that we expect but VSS.require doesn’t import any type information so we can fall back to a separate require which we can use, just purely for type information:

import RmContractDefintitions = require("ReleaseManagement/Core/Contracts"); import RmClientDefinitions = require('ReleaseManagement/Core/RestClient');

So by using the type information from the plain require and the actual objects returned from VSS.require we can get full type information including compile-time checking and intellisense:

import RmContractDefintitions = require("ReleaseManagement/Core/Contracts"); import RmClientDefinitions = require('ReleaseManagement/Core/RestClient'); ...

VSS.require([‘ReleaseManagement/Core/RestClient’], (RmClientService) => {

let rmClient : RmClientDefinitions.ReleaseHttpClient4 = RmClientService.getClient();

This compared to no types and no intellisense:

Obviously, this code won’t be found to be wrong until a pull publish, reload cycle which could be minutes depending on the type of extension.