TypeScript is a superset of JavaScript, that means valid JavaScript code is valid TypeScript code, thus JavaScript libraries can be used in TypeScript. TypeScript introduces optional static typing which makes much easier to scale with larger development teams. TypeScript compiles to JavaScript for it to be executed/interpreted. We focus here only on TypeScript, not on using it on web development. We are trying to focus on one challenge only at a time. Here we are focusing on TypeScript only. This also means that we will not talk about alternative ways to do the same thing that are coming from JavaScript.
Many of the increments and type checks that TypeScript add does not carry over when the file is compiled to JavaScript. There is a strong dependency on the IDE or the code editor itself to give quick feedback on what is wrong. That said, I found that the feedback in the IDE to be very detailed an informative. That said, we can run the TypeScript compiler with the flag --strict
for it not to avoid this leniency.
Basic IO
For the purposes of this document we will use console.log(...)
for writing to std out
Variables, Constants and Basic Types
Declaration
let
declare a variableconst
declare a constant- While JavaScript’s
var
is still available, it is not recommended since variables declared using var
are not scoped among other problems. - If no type is provided when declaring a variable or constant, the type
any
is assumed - To explicitly provide the type we write the variable/constant name followed by
:
then the type. let name: number = "Jane"
Deconstructing
- Deconstructing is the act of taking a complex variable and assign its parts (deconstruct it) to more than one variable.
- We will show here a simple example and defer the discussion of deconstruction to be close to each associated type
1
2
3
4
5
| let array = [1, 2];
let [first, second] = array;
console.log(first); // Prints 1
console.log(second); // Prints 2
|
Commonly Used Data Types
Any
- As the name implies this could be anything, which is the same behaviour JavaScript has. This is useful mostly in interfacing with other JavaScript libraries.
Void
- Indicates an absence of value, usually used as the return type of a function that returns nothing.
- A variable of type
void
can only be assigned either undefined
or null
only if --strictNullChecks
is not specified
Never
never
is a type indicating that the computation will never terminate or will terminate with an exceptionnever
is a subtype of all other typesNull and undefined
null
and undefined
are subtypes of all other types except for never
- when using the
--strictNullChecks
flagnull
is only assignable to any
undefined
is assignable to either any
or void
Object
object
is a type that represents any non-primitive type
Strings
- Either
'
or "
can be used to surround string literalsString Interpolation
- A back tick can be used for_template strings_, which can span multiple lines and include an evaluable expressions in the format
${ expr }
1
2
3
| let world = "World!";
let template = `Hello ${world}`;
console.log(template); // `Hello World!`
|
String Literal Types
- In this case the type annotation is a string literal. This indicate that the variable can only have a string equivalent to that is of the type.
let green: "Green"
can only have the value “Green”.let trafficLight: "Red" | "Green" | "Yellow"
means that this value can only be either of the three. This syntax is a Union Type which is discussed separately- Variable of string literal types may be considered a subtype of string as they can be converted to strings but not the other way around
- The above is not enforced in compile or run time, but the IDE will complain
1
2
3
4
5
6
7
8
9
10
11
| let red: "Red";
let green: "Green";
let yellow: "Yellow";
let trafficLight: "Red" | "Green" | "Yellow";
red = "Red";
green = "Green";
// red = green; // The IDE shall complain and a compile time error if `--strict` compiler flag is set
trafficLight = "Yellow";
// green = trafficLight; // The IDE shall complain and a compile time error if `--strict` compiler flag is set
let stringRepresentation = green; // That is absolutely OK!
|
Collections
Arrays
- We can declare an array using one for two equivalent notations
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
- Elements of the array can be accesses by index, and we can add elements in arbitrary indexes as well
1
2
3
4
| let array: number[] = [1, 2, 3];
array[4] = 100;
console.log(array); // Prints `[ 1, 2, 3, <1 empty item>, 100 ]`
console.log(array[5]); // Prints `undefined`
|
Tuples
- Tuples behaviour is more aligned with a conventional array, except the type of each element has to be predetermined and can’t be changed
1
2
3
4
| let tuple: [number, string] = [1, "hello"];
// tuple[2] = "hi"; // Compile time error
console.log(tuple);
// console.log(tuple[4]); // Compile time error
|
Enums
- Enum elements start at index
0
by default similar to an array. We can however set either the first element and the remaining elements will increment by one, or set each element index explicitly.
1
2
3
4
5
6
7
8
| enum Color {Red, Green, Blue}
console.log(Color[2]); // Prints `Blue`
enum Color2 {Red = 1, Green, Blue}
console.log(Color2[2]); // Prints `Green`
enum Color3 {Red = 2, Green = 4, Blue = 6}
console.log(Color3[2]); // Prints `Red`
|
- Assigning an enum element to a variable is straightforward enough
let color = Color.Green
- We can expand an enum after the fact given that we explicitly give a value for the first element in the expansion
1
2
3
| enum Color{Orange=10, Yellow}
console.log(Color[11]); // Prints `Yellow`
console.log(Color[100]); // Prints `undefined`
|
- This also compiles, but I am not going to the rabbit whole of trying to understand what is going on ¯_(ツ)_/¯
1
2
3
4
5
6
7
| enum Color4 {Red, Green} // IDE complains about unused enum
enum Color4 {Yellow = 0, Orange}
console.log(Color4)
console.log(Color4[0]); // Prints `Yellow`
console.log(Color4.Yellow); // Prints `0`
console.log(Color4.Red); // Prints `0`
|
Casting and Type Guards
as
as
operator for type assertion (casting). It is not checked at compile time or runtime. The generated JavaScript omits this check altogether- Alternatively we can write
person as Teacher
as <Teacher> person
. Both expressions have the same effect
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| interface Person {
}
class Teacher implements Person {
teach() {
console.log("I am teaching");
}
}
let person: Person = new Teacher();
if (person instanceof Teacher) {
person.teach();
}
let teacher = person as Teacher;
teacher.teach();
let wrongCast = person as string;
console.log(wrongCast); // Happily prints `Teacher{}`
console.log(wrongCast.charAt(0)); // throws an exception `charAt is not a function`
|
instanceof
instanceof
keyword is a boolean check of whether an object is of some given type. The object will be cast automatically within the scope in which type is checked
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| interface Person {
}
class Teacher implements Person {
teach() {
console.log("I am teaching");
}
}
let person: Person = new Teacher();
if (person instanceof Teacher) {
person.teach(); // Method `teach()` is not available out of this scope
}
|
typeof
typeof
keyword is used to get the type of something, usually used to check for tye type of primitive types since they are not objects and hence instanceof
will not work.- Used to check for one of the types
string
, number
, bigint
, boolean
, symbol
, undefined
, object
, function
1
2
3
4
5
6
7
8
9
| let mayHaveAnyType: any = 4;
console.log(typeof mayHaveAnyType); // Prints `number`
mayHaveAnyType = "hello";
console.log(typeof mayHaveAnyType); // Prints `string
console.log(mayHaveAnyType.charAt(0)); // `charAt(0)` is accessible outside of the type scope. It will compile, but may fail in run time if the type is wrong
if (typeof mayHaveAnyType == "string") {
console.log(mayHaveAnyType.charAt(0));
}
|
Resources
Comments powered by Disqus.