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.