// 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 = (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); */