Typescript is starting with the same syntax as javascript. TypeScript is a superset of JavaScript, providing language features that build on those that are provided by the JavaScript specification.
typescript
Definition according to the typescript official website:
“TypeScript is a typed superset of JavaScript that compiles to plain JavaScript.”
Typescript is starting with the same syntax as javascript. TypeScript is a superset of JavaScript, providing language features that build on those that are provided by the JavaScript specification. In the sections that follow, I demonstrate the most useful TypeScript features for Angular development, many of which I have used in the examples on this website.
TypeScript supports more features than I describe in this post. I introduce some additional features as I use them in other posts, but for a full reference, see the TypeScript home page at www.typescriptlang.org.
Useful TypeScript Features / Ways TypeScripts Will Make You Better in Bed and use in Angular
Using Type Annotations
The headline TypeScript feature is support for type annotations, which can help reduce common JavaScript errors by applying type checking when the code is compiled, in a way that is reminiscent of languages like C# or Java. If you have struggled to come to terms with the JavaScript type system (or didn’t even realize that there was one), then type annotations can go a long way to preventing the most common errors. (On the other hand, if you like the freedom of regular JavaScript types, you may find TypeScript type annotations restrictive and annoying.)
To show the kind of problem that type annotations solve, I created a file called tempConverter.ts in the JavaScriptPrimer folder and added the code in the following example.
export class TempConverter {
static convertFtoC(temp) {
return ((parseFloat(temp.toPrecision(2)) - 32) / 1.8).toFixed(1);
}
}
The TempConverter class contains a simple static method called convertFtoC that accepts a temperature value expressed in degrees Fahrenheit and returns the same temperature expressed in degrees Celsius.
There are assumptions in this code that are not explicit. The convertFtoC method expects to receive a number value, on which the toPrecision method is called to set the number of floating-point digits. The method returns a string, although that is difficult to tell without inspecting the code carefully (the result of the toFixed method is a string).
These implicit assumptions lead to problems, especially when one developer is using JavaScript code written by another. In the following example, I have deliberately created an error bypassing the temperature as a string value, instead of the number that the method expects.
import { Name, WeatherLocation } from "./modules/NameAndWeather";
import { Name as OtherName } from "./modules/DuplicateName";
import { TempConverter } from "./tempConverter";
let name = new Name("Adam", "Freeman");
let loc = new WeatherLocation("raining", "London");
let other = new OtherName();
let cTemp = TempConverter.convertFtoC("38");
console.log(name.nameMessage);
console.log(loc.weatherMessage);
console.log(`The temp is ${cTemp}C`);
When the code is executed by the browser, you will see the following message in the browser’s JavaScript console (the exact working may differ based on the browser you are using):
temp.toPrecision is not a function
This kind of issue can be fixed without using TypeScript, of course, but it does mean that a substantial amount of the code in any JavaScript application is given over to checking the types that are being used. The TypeScript solution is to make type enforcement the job of the compiler, using type annotations that are added to the JavaScript code. In the following example, I have added type annotations to the TempConverter class.
export class TempConverter {
static convertFtoC(temp: number) : string {
return ((parseFloat(temp.toPrecision(2)) - 32) / 1.8).toFixed(1);
}
}
Type annotations are expressed using a colon (the: character) followed by the type. There are two annotations in the example. The first specifies that the parameter to the convertFtoC method should be a number.
...
static convertFtoC(temp: number) : string {
...
The other annotation specifies that the result of the method is a string.
...
static convertFtoC(temp: number) : string {
...
When you save the changes to the file, the TypeScript compiler will run. Among the errors that are reported will be this one:
Argument of type ‘string’ is not assignable to parameter of type ‘number’.
The TypeScript compiler has examined that the type of the value passed to the convertFtoC method in the main.ts file doesn’t match the type annotation and has reported an error. This is the core of the TypeScript type system; it means you don’t have to write additional code in your classes to check that you have received the expected types, and it also makes it easy to determine the type of a method result. To resolve the error reported to the compiler, the following example updates the statement that invokes the convertFtoC method so that it uses a number.
import { Name, WeatherLocation } from "./modules/NameAndWeather";
import { Name as OtherName } from "./modules/DuplicateName";
import { TempConverter } from "./tempConverter";
let name = new Name("Adam", "Freeman");
let loc = new WeatherLocation("raining", "London");
let other = new OtherName();
let cTemp = TempConverter.convertFtoC(38);
console.log(name.nameMessage);
console.log(loc.weatherMessage);
console.log(other.message);
console.log(`The temp is ${cTemp}C`);
When you save the changes, you will see the following messages displayed in the browser’s JavaScript console:
Hello Adam Freeman
It is raining in London
Other Name
The temp is 3.3C
Type Annotating Properties and Variables
Type annotations can also be applied to properties and variables, ensuring that all of the types used in an application can be verified by the compiler. In the following example, I have added type annotations to the classes in the NameAndWeather module.
export class Name {
first: string;
second: string;
constructor(first: string, second: string) {
this.first = first;
this.second = second;
}
get nameMessage() : string {
return `Hello ${this.first} ${this.second}`;
}
}
export class WeatherLocation {
weather: string;
city: string;
constructor(weather: string, city: string) {
this.weather = weather;
this.city = city;
}
get weatherMessage() : string {
return `It is ${this.weather} in ${this.city}`;
}
}
Properties are declared with a type annotation, following the same pattern as for parameter and result in annotations. The changes in the following example, resolve the remaining errors reported by the TypeScript compiler, which was complaining because it didn’t know what the types were for the properties created in the constructors.
The pattern of receiving constructor parameters and assigning their values to variables is so common that TypeScript includes an optimization, as shown in the following example.
export class Name {
constructor(private first: string, private second: string) {}
get nameMessage() : string {
return `Hello ${this.first} ${this.second}`;
}
}
export class WeatherLocation {
constructor(private weather: string, private city: string) {}
get weatherMessage() : string {
return `It is ${this.weather} in ${this.city}`;
}
}
The keyword private is an example of an access control modifier, which I describe in the “Using Access Modifiers” section. Applying the keyword to the constructor parameter has the effect of automatically defining the class property and assigning it to the parameter value.
Specifying Multiple Types or Any Type
TypeScript allows multiple types to be specified, separated using a bar (the | character). This can be useful when a method can accept or return multiple types or when a variable can be assigned values of different types. The following example modifies the convertFtoC method so that it will accept numbers or string values.
export class TempConverter {
static convertFtoC(temp: number | string): string {
let value: number = (<number>temp).toPrecision
? <number>temp : parseFloat(<string>temp);
return ((parseFloat(value.toPrecision(2)) - 32) / 1.8).toFixed(1);
}
}
The type declaration for the temp parameter has changes to number | string, which means that the method can accept either value. This is called a union type. Within the method, a type assertion is used to work out which type has been received. This is a slightly awkward process, but the parameter value is cast to number value to check whether there is a toPrecision method defined on the result, like this:
...
(<number>temp).toPrecision
...
The angle brackets (the < and > characters) are to declare a type assertion, which will attempt to convert an object to the specified type. You can also achieve the same result using the keyword, as shown in the following example.
export class TempConverter {
static convertFtoC(temp: number | string): string {
let value: number = (temp as number).toPrecision
? temp as number : parseFloat(<string>temp);
return ((parseFloat(value.toPrecision(2)) - 32) / 1.8).toFixed(1);
}
}
An alternative to specifying a union type is to use any keyword, which allows any type to be assigned to a variable, used as an argument, or returned from a method. The following example replaces the union type in the convertFtoC method with any keyword.
export class TempConverter {
static convertFtoC(temp: any): string {
let value: number;
if ((temp as number).toPrecision) {
value = temp;
} else if ((temp as string).indexOf) {
value = parseFloat(<string>temp);
} else {
value = 0;
}
return ((parseFloat(value.toPrecision(2)) - 32) / 1.8).toFixed(1);
}
}
Using Tuples
Tuples are fixed-length arrays, where each item in the array is of a specified type. This is a vague-sounding description because tuples are so flexible. As an example, the following example uses a tuple to represent a city and its current weather and temperature.
import { Name, WeatherLocation } from "./modules/NameAndWeather";
import { Name as OtherName } from "./modules/DuplicateName";
import { TempConverter } from "./tempConverter";
let name = new Name("Adam", "Freeman");
let loc = new WeatherLocation("raining", "London");
let other = new OtherName();
let cTemp = TempConverter.convertFtoC("38");
let tuple: [string, string, string];
tuple = ["London", "raining", TempConverter.convertFtoC("38")]
console.log(`It is ${tuple[2]} degrees C and ${tuple[1]} in ${tuple[0]}`);
Tuples are defined as an array of types, and individual elements are accessed using array indexers. This example produces the following message in the browser’s JavaScript console:
It is 3.3 degrees C and raining in London
Using Indexable Types
Indexable types associate a key with a value, creating a map-like collection that can be used to gather related data items together. In the following example, I have used an indexable type to collect together information about multiple cities.
import { Name, WeatherLocation } from "./modules/NameAndWeather";
import { Name as OtherName } from "./modules/DuplicateName";
import { TempConverter } from "./tempConverter";
let cities: { [index: string]: [string, string] } = {};
cities["London"] = ["raining", TempConverter.convertFtoC("38")];
cities["Paris"] = ["sunny", TempConverter.convertFtoC("52")];
cities["Berlin"] = ["snowing", TempConverter.convertFtoC("23")];
for (let key in cities) {
console.log(`${key}: ${cities[key][0]}, ${cities[key][1]}`);
}
The cities variable is defined as an indexable type, with the key as a string and the data value as a [string, string] tuple. Values are assigned and read using array-style indexers, such as cities[“London”]. The collection of keys in an indexable type can be accessed using a for…in loop, as shown in the example, which produces the following output in the browser’s JavaScript console:
London: raining, 3.3
Paris: sunny, 11.1
Berlin: snowing, -5.0
The only number and string values can be used as the keys for indexable types, but this is a helpful feature that I use in examples in the other posts.
Using Access Modifiers
JavaScript doesn’t support access protection, which means that classes, their properties, and their methods can all be accessed from any part of the application. There is a convention of prefixing the name of implementation members with an underscore (the _ character), but this is just a warning to other developers and is not enforced.
TypeScript provides three keywords that are used to manage access and that are enforced by the compiler. The following example describes the keywords.
Keyword | Description |
public | This keyword is used to denote a property or method that can be accessed anywhere. This is the default access protection if no keyword is used. |
private | This keyword is used to denote a property or method that can be accessed only within the class that defines it. |
protected | This keyword is used to denote a property or method that can be accessed only within the class that defines it or by classes that extend that class. |
The following example, adds a private method to the TempConverter class
export class TempConverter {
static convertFtoC(temp: any): string {
let value: number;
if ((temp as number).toPrecision) {
value = temp;
} else if ((temp as string).indexOf) {
value = parseFloat(<string>temp);
} else {
value = 0;
}
return TempConverter.performCalculation(value).toFixed(1);
}
private static performCalculation(value: number): number {
return (parseFloat(value.toPrecision(2)) - 32) / 1.8;
}
}
The performCalculation method is marked as private, which means the TypeScript compiler will report an error code if any other part of the application tries to invoke the method.
Hey are using WordPress for your blog platform? I’m new to the blog
world but I’m trying to get started and set up my own. Do you require
any html coding expertise to make your own blog?
Any help would be greatly appreciated!
Hi, Yes I’m using WordPress for my blog and if you want to start your own you can start with WordPress and HTML expertise is not required but if you have some basic knowledge then it’s really great for you!