aboutsummaryrefslogtreecommitdiff
path: root/TypeScript/ts-testing.ts
blob: 59758f3709d55f3dafd224a8db7d1487b181d68d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
// Notes taken from: https://www.youtube.com/watch?v=NjN00cM18Z4 and https://www.youtube.com/watch?v=xPEMup5SPTM

/*******************/
/* Variable scopes */
/*******************/

function doSmth() {
	if (true) {
		// With "var" the scope is the nearest function
		var variable = 1;
	}
	console.log(variable);

	if (true) {
		// With "let" the socope is the nearest block
		let anotherVariable = 2;
	}

	// The following line shows you an error
	// IMPORTANT: if you build it with the following line uncommented, 
	// tsc will give you an error, but will make let anotherVariable to var anotherVariable in the js file

	// console.log(anotherVariable)
}

/***********************/
/* Declaring variables */
/***********************/

// Declaring a variable and initializing (giving it a value when declared)
// will cast the value to a specific data type (doesn't matter if you use "let" or "var")
var num  = 5;
let num1 = -5;
let arr = [1, 2, 3]; // declare an initilize an int array
num  = 16;
num1 = -16;
arr = [5, 6, 7];

// The following two lines give you an error, if uncommented

// num  = "Hello";
// num1 = "Hello";
// arr = ["Hello", "World"];

// Declaring a variable without initializing it will give it the type "any",
// meaning it can be assigned whatever type of value
let anyVariable;
anyVariable = 16;
anyVariable = "Hello";
anyVariable = true;

/****************************/
/* Declaring variable types */
/****************************/

// This is how to declare variables of given type (without initializing)
let myNum: number; // no difference between doubles, ints, longs, ...
let myBool: boolean;
let myString: string;
let myAny: any;

// This is how to declare arrays (without initializing)
let myNumArray: number[];
let myAnyArray: any[];

// Note: you can also initilize an array when declaring it
let numArray: number[] = [1, 2, 3];
// but in this case TypeScript will take care of data type for you,
// so you can simplify it to this
let numArray1 = [1, 2, 3];

// You can declare that a variable can be multiple types with the pipe (|) symbol
let myNumOrString: number | string;
// You can also use as many as you like
let myNumOrStringOrBool: number | string | boolean;
// You can also apply this to arrays
let myNumOrStringArray: (number | string)[];

// Declaring enums (the same way as in C#)

enum ColorCodes {
	Red = 0,
	Green = 1,
	Blue = 2
}

// Type insertion: when you tell the compiler that a given value is of given type

let message;
message = "Hello World";

// Because message isn't initilized, it's type is "any"
// but when we want to do something like this, intellisense won't tell you that 
// the string function "charAt" is available (but the code will compile just fine)
let firstCharInMessage = message.charAt(0);

// If we want to get help in situations, where the type is "any",
// but we want a function of a specific type, we can do type insertion
firstCharInMessage = (<string>message).charAt(0);
// you can also do it like this, both ways work the same, it's up to preference which one to use
firstCharInMessage = (message as string).charAt(0);

/*************/
/* Functions */
/*************/

// Note: methods are functions inside objects, in C# everything is an object, so we have only methods there, but in TypeScript it isn't like that
function add(num1, num2) {
	return num1 + num2;
}
// You can declare parameter data types
function add1(num1: number, num2: number) {
	return num1 + num2;
}
// You can also declare the return type
function add2(num1: number, num2: number): number {
	return num1 + num2;
}

// Let's say you want to assign a function to a variable
// In Javascript you're gonna do it like this
let myFunction = function () {
	console.log("Test");
}

// Well, in TypeScript, you can do the same with an arrow function, that
// is equavalent to the C# lambda function
myFunction = () => console.log("Test");
// You can also add a body to it
myFunction = () => {
	console.log("Test");
}
// And you can add parameters
let myOtherFunction = (message) => {
	console.log(message);
}

/******************/
/* Custom objects */
/******************/

// You can easily create objects with any properties with curly brackets
let myObject = {
	Name: "Pesho",
	Age: 20,
	Married: false
}

// If you want to give an object as a function parameter
let drawPoint = (pointLocation) => {
	// ...
}
// you can do it like this, without having a class/interface (DON'T do this)
drawPoint({ x: 1, y: 3 });

// But to make sure that the given object has some proerties, we can do 
// inline annotation in our function
// This declares that pointLocation's type is an object, that has two number properties: x and y
drawPoint = (pointLocation: { x: number, y: number }) => {
	// ...
}

/*******************************/
/* Object oriented programming */
/*******************************/

// Making an interface
interface IPerson {
	name: string,
	age: number,
	married: boolean,
	eat: (food) => void // this is how to declare a function type
}

// Making a class
class Person {
	name: string;
	age: number;
	married: boolean;

	constructor(name: string, age: number, married?: boolean) {
		this.name = name;
		this.age = age;
		// The married? means the married variable is optional, you don't need to give it a value
		this.married = married;
	}

	eat() {
		// ...
	}
}

let person = new Person("Gosho", 16);
person.eat();

// Access modifiers
// Note: all properties and methods are public by default, no need to use the word "public"
class BankAccount {
	private ID: number;
	
	protected deposit(money: number) {
		// ...
	}
}

// You can create properties via the constructor
class Person1 {
	constructor(public name: string, public age: number, public married?: boolean) {
		// You can access the name, age and married properties, because by adding an
		// access modifier in the constructor creates them
		this.name = name;
		this.age = age;
		this.married = married;
	}
}

let person1 = new Person1("Gosho", 16, false);
console.log(person1.name + " " + person1.age + " " + person1.married);

class Person2 {
	constructor(public name: string, public age: number, public married?: boolean) {
		// In this case, and in Person1 class, you don't even need to assign the values, 
		// they are automatically assigned by the constructor
	}
}

let person2 = new Person2("Gosho", 16, false);
console.log(person2.name + " " + person2.age + " " + person2.married);


// Getters and setters can be made to be just simple functions
class Person3 {
	constructor(public name: string, private age: number, public married?: boolean) {
	}

	getAge() {
		return this.age;
	}

	setAge(value: number) {
		// And while we're at it, here is an example of simple data validation
		if (value < 0 || value > 140)
			throw new Error("Age must be between 0 and 140");

		this.age = value;
	}
}

let person3 = new Person3("Gosho", 16, false);
person3.setAge(17);
console.log(person3.getAge());

// Or you can specifically implement them
class Person4 {
	constructor(public name: string, private age: number, public married?: boolean) {
	}

	get Age() {
		return this.age;
	}

	set Age(value: number) {
		// And while we're at it, here is an example of simple data validation
		if (value < 0 || value > 140)
			throw new Error("Age must be between 0 and 140");

		this.age = value;
	}
}

var person4 = new Person4("Gosho", 16, false);
person4.Age = 16;
console.log(person4.Age);

// IMPORTANT NOTE ON CONVENTIONS
// it's a convention to use cammelCase for almost everything, including properties
// so, private values are given an underscore in the beginning of the name

class Person5 {
	constructor(public name: string, private _age: number, public married?: boolean) {
	}

	// We can now use the word "age" with lowercase letters for the setter and getter
	// before it was causing issues with the variable being also named "age", so we had
	// to make the setter and getter "Age", which isn't a good convention
	get age() {
		return this._age;
	}

	set age(value: number) {
		// And while we're at it, here is an example of simple data validation
		if (value < 0 || value > 140)
			throw new Error("Age must be between 0 and 140");

		this._age = value;
	}
}

var person5 = new Person5("Gosho", 16, false);
person5.age = 16;
console.log(person5.age);

/***********/
/* Modules */
/***********/

// Unless configured otherwise, everything in a TypeScript file is public to everything else in that file (scope)
// What if we want to have something be accessable from other files?
// Things that we can acccess from other files are called modules.
// Note: examples are of two different files, that's why they are all commented

// Let's say we want to put the class Person into another file, called person.ts

/* Inside person.ts:
 *
 * // We need the word "export" to signify that it can be accessed from the outside world
 * export class Person {
 * 		constructor(public name: string, public age: number, public married?: boolean) {
 * 		}
 * }
 *
 */

/* Inside program.ts
 *
 * // To get stuff from the outside world, you use "import"
 * // in the curly brackets you specify what you want to get
 * // and after that, in the quotes, you specify where you want to get it from (the path)
 * // Note: as you can see, we have './person' and not './person.ts', because 'person' is the name of the module, while 'person.ts' is the name of the file
 * import { Person } from './person';
 *
 * let myPerson = new Person("Gosho", 16, false);
 */