MCederholm

How to: Create a TS/JSAPI project template for VS2017

Blog Post created by MCederholm on Jan 10, 2020

Being a user of Microsoft Visual Studio since version 6.0, I prefer it as a one-stop shop for as many kinds of development as possible, including C++, VB, C#, Python, and HTML5/TypeScript projects. One feature of VS that I really like is the ability to create project templates. VS2015 included a project template for TypeScript, but it was ugly as sin. VS2017 dropped it, but failed to provide a viable alternative; being lazy, I continued to use the same version available online:

This must stop! Sometimes, you just have to get your hands dirty, so I decided to create my own project template from scratch. Fortunately, the TypeScript documentation has sections on Integrating with Build Tools, and Compiler Options in MSBuild, which provided valuable assistance. Also, see the MSBuild documentation and How to: Create project templates for more information.

 

Prerequisites:

The TypeScript website has download links to install the latest version for a number of IDEs, including VS2017. In addition, since the TypeScript folks now prefer you to use npm to install typings; you should install Node.


Warning! If you are behind a corporate firewall, you may run into this error when you try to use npm to install typings:

   npm ERR! code UNABLE_TO_GET_ISSUER_CERT_LOCALLY

If you see that, try setting this configuration at the command prompt:

   npm config set strict-ssl false

 

Create a generic TypeScript project:

While, formally, the best approach would be to create a new project type, my lazy approach recycles the C# project type and redefines the build targets (but there is a disadvantage – see below). The first step is to create a blank solution in VS2017 named “TypeScriptProjectTemplates.” In Explorer or the Command Prompt, navigate to the solution folder and create a subfolder named “BasicTypeScriptTemplate.” In that folder, create a file named “BasicTypeScriptTemplate.csproj,” containing the following text:

 

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.Default.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <OutputType>Library</OutputType>
    <StartupObject />
    <OutputPath>.\</OutputPath>
    <IntermediateOutputPath>vs\</IntermediateOutputPath>
  </PropertyGroup>
  <PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">12.0</VisualStudioVersion>
  </PropertyGroup>
  <PropertyGroup>
    <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
    <TypeScriptModuleKind>amd</TypeScriptModuleKind>
    <TypeScriptNoImplicitAny>true</TypeScriptNoImplicitAny>
    <TypeScriptESModuleInterop>true</TypeScriptESModuleInterop>
    <TypeScriptJSXEmit>react</TypeScriptJSXEmit>
    <TypeScriptJSXFactory>tsx</TypeScriptJSXFactory>
    <TypeScriptTarget>es5</TypeScriptTarget>
    <TypeScriptExperimentalDecorators>true</TypeScriptExperimentalDecorators>
    <TypeScriptPreserveConstEnums>true</TypeScriptPreserveConstEnums>
    <TypeScriptSuppressImplicitAnyIndexErrors>true</TypeScriptSuppressImplicitAnyIndexErrors>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)' == 'Debug'">
    <TypeScriptRemoveComments>false</TypeScriptRemoveComments>
    <TypeScriptSourceMap>true</TypeScriptSourceMap>
  </PropertyGroup>
  <PropertyGroup Condition="'$(Configuration)' == 'Release'">
    <TypeScriptRemoveComments>true</TypeScriptRemoveComments>
    <TypeScriptSourceMap>false</TypeScriptSourceMap>
  </PropertyGroup>
  <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets" Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\TypeScript\Microsoft.TypeScript.targets')" />
  <Target Name="Build" DependsOnTargets="CompileTypeScript">
  </Target>
  <Target Name="Rebuild" DependsOnTargets="CompileTypeScript">
  </Target>
  <Target Name="Clean" Condition="Exists('$(TSDefaultOutputLog)')">
    <ItemGroup>
      <TSOutputLogsToDelete Include="$(TSDefaultOutputLog)" />
    </ItemGroup>
    <ReadLinesFromFile File="@(TSOutputLogsToDelete)">
      <Output TaskParameter="Lines" ItemName="TSCompilerOutput" />
    </ReadLinesFromFile>
    <Delete Files="@(TSCompilerOutput)" Condition=" '@(TSCompilerOutput)' != '' " />
    <Delete Files="@(TSOutputLogsToDelete)" />
    <!-- <RemoveDir Directories="$(IntermediateOutputPath)" /> -->
  </Target>
</Project>

 

In VS2017, add the existing project to the solution. Within the project, create an “app” subfolder, and add a new TypeScript file named “main.ts,” containing the following text:

class Student {
     fullName: string;
     constructor(public firstName: string, public middleInitial: string, public lastName: string) {
          this.fullName = firstName + " " + middleInitial + " " + lastName;
     }
}

interface Person {
     firstName: string;
     lastName: string;
}

function greeter(person: Person) {
     return "Hello, " + person.firstName + " " + person.lastName;
}

let user = new Student("Jane", "M.", "User");

document.body.textContent = greeter(user);

 

In the project folder, add an HTML Page file named “index.html,” containing the following text:

<!DOCTYPE html>
<html>
<head>
     <title>TypeScript Greeter</title>
</head>
<body>
     <script src="./app/main.js"></script>
</body>
</html>

 

At this point, the project should look like this in Solution Explorer:

 

Building or rebuilding the project will generate TypeScript compiler output, and a file named “Tsc.out” will be created in a subfolder named “vs”. The “Tsc.out” file defines the compiler output to delete when cleaning the project; cleaning the project will also delete that file. [Note that if you build and clean “Release” without cleaning “Debug” beforehand, the source map files will still remain.]

 

Export the project template:

At this point, if you export the project to a template, you have a generic TypeScript project template. However, it will be displayed under the “Visual C#” category. If you want it to appear under the “TypeScript” category, there are additional steps to take. First, unzip the template to a new folder and edit “MyTemplate.vstemplate:”

 

Change the “ProjectType” value from “CSharp” to “TypeScript”. Zip the contents of the folder to a new zip file, and the template is ready to use.

 

Building a JSAPI project template:

Now that we have a basic TypeScript project template, the next step is to use it to create a template for a simple JavaScript API project.


First, create a new project, called “ArcGIS4xTypeScriptTemplate,” using the template created above. Open a Command Prompt, navigate to the project folder, and enter the following commands:

   npm init --yes
   npm install --save @types/arcgis-js-api

Back in Solution Explorer, select the project and click the “Show All Files” button. Select the “node_modules” folder, “package.json,” and “package-lock.json,” right-click, and select “Include In Project.” Finally, replace the contents of “main.ts” and “index.html” with the text given at the JSAPI TypeScript walk-through. Your project should now look like this:

 

You may notice that the “import” statements in “main.ts” are marked as errors, even though the esModuleInterop flag is set in the project:

 

This appears to be a defect in the Visual Studio extension. The project will build without errors, and the resulting page will load correctly. If it annoys you, you can always revert to the older AMD style statements:

 

At this point, you’re ready to export the template.

 

On a final note:

The JavaScript API is updated frequently, which means that you may also want to keep your project templates up to date. Rather than updating the source project and repeating the export steps, you might want to consider keeping the unzipped template folders in a standard location, and updating those directly. Then, all you have to do is zip them to create the updated template.

Attachments

Outcomes