Learn TypeScript in Construct, part 14: Onwards

UpvoteUpvote 5 DownvoteDownvote

Index

Features on these Courses

Stats

181 visits, 227 views

Tools

Translations

This tutorial hasn't been translated.

License

This tutorial is licensed under CC BY-NC 4.0. Please refer to the license text if you wish to reuse, share or remix the content contained within this tutorial.

Published on 31 Jul, 2025. Last updated 1 Aug, 2025

Odds and ends

Here are a few more details about TypeScript to mention before we finish that didn't fit in to any previous section, or were skipped over for simplicity.

Prefer const instead of let

Throughout this guide we generally used let to declare variables. However it's a good practice to actually use const for any variable that is not reassigned. The code below shows an example of where you might prefer const over let.

function getSomething()
{
	// 'x' is never reassigned, so prefer 'const'
	// over 'let' for it.
	const x = getValue();
	return x * 2;
}

Note this doesn't mean x is always the same value - it just means it never changes after it is initialized. This can be a useful thing to know when reading over more complicated functions.

The TypeScript examples in Construct's Example Browser are written to prefer const.

More about Construct's types

Earlier we mentioned that the type representing a Sprite object type, e.g. at runtime.objects.Sprite, is IObjectType. This is a simplification. Previously we covered generics, such as how the type for a Set of numbers is Set<number>. Similarly to that, many of Construct's built-in types use generics. This is to ensure methods like getFirstInstance() return an instance type specific to the object type, rather than a general-purpose one. It also uses generics to handle the kinds of events you can listen for with addEventListener. Therefore the full TypeScript type of a Sprite object type is more like IObjectType<InstanceType.Sprite, ObjectClassEventMap<InstanceType.Sprite>>. However, don't be alarmed: you can just read that as meaning "IObjectType for InstanceType.Sprite". Generally you won't have to worry about the full types, as TypeScript's type inference generally means you don't have to write out the full types of things very often.

do-while

The do...while statement is a fairly uncommonly used variant on the standard while loop that always runs its statements at least once. Sometimes it's a useful way to write a loop, and is worth knowing about.

Legacy features

JavaScript has various old features that basically shouldn't be used any more, which then were inherited by TypeScript. You may come across them if you work with existing pieces of older code. These include:

  • var for declaring variables, which has some unusual and fairly complicated quirks, which is why let is now preferred.
  • There's also a kind of loop called for...in, but this has been superseded by the more modern for...of loop used in this guide.
  • Construct always uses modules. Non-module scripts, also known as "classic" scripts, work slightly differently. This does not affect Construct as it always uses module scripts, but classic scripts have some small differences, notably that top-level variables are global rather than scoped to the file, and the inability to use import and export statements (but you can use dynamic imports).

Quirks

Like most programming languages, TypeScript has some quirks - unusual things that work strangely or in unexpected ways - most of which are inherited from JavaScript. These are best avoided. They are generally just obscurities, accidents or leftovers from poor design decisions years ago that are now too difficult to change. Don't write code that relies on them. However sometimes you have to be aware of them, if only to make sure you steer clear.

TypeScript actually fixes a number of quirks of JavaScript, such as disallowing nonsensical calculations like adding a number to an array. However some quirks remain.

Type conversions

Some type conversions might produce unexpected results:

// Convert empty array to number
Number([])		// 0

// Convert empty object to number
Number({})		// NaN

// Adding with a string returns a string
1 + "1"			// "11" (string)

Array indices

If you access an array at a fractional index, it will return undefined.

["😀", "👾"][0]		// "😀"
["😀", "👾"][0.5]	// undefined

The reason for this is a very strange part of JavaScript where technically the array elements are string properties that are a string of a number. Accessing arr[0.5] actually accesses a property named "0.5", i.e. arr["0.5"], which does not exist in the array; accessing a non-existent property returns undefined.

In TypeScript this is a nasty gap in the type checker. TypeScript assumes that for an array of strings, accessing an element will also return a string - if it allowed for the possibility of undefined with every access, it would be a constant irritation. However that means with the code let value = ["😀", "👾"][0.5];, the type of value is string, but it really contains undefined. TypeScript will allow you to use the value like it's really a string, but the code would crash at runtime. Oops!

Many other programming languages avoid this by using a number for array indices that is automatically rounded. However since this does not happen in TypeScript, we have to make sure array indices are always whole numbers. Using Math.floor() on the array index is a good way to do that. For example to access a random element from an array, use arr[Math.floor(Math.random() * arr.length)], since Math.random() returns a fractional number, so it must be rounded down to a whole number.

More quirks

You'll probably come across other quirks or surprising things about JavaScript and TypeScript as you work more with it. Remember to try to avoid surprising parts of TypeScript - it's best to write clear code, and using obscure or accidental features of TypeScript makes your code harder to understand.

  • 0 Comments

Want to leave a comment? Login or Register an account!