/* Inicializar un paquete: npm init -y Añadir el compilador typescript npm add -s typescript Ejecutar el compilador typescript npx tsc Inicializar projecto typescript con codigo en src y salida en lib npx tsc --init --rootdir src --outdir lib Ahora la ejecución compila el codigo npx tsc Dejarlo compilando npx tsc --watch */ let message: string = "Hello world"; console.log(message); /* "use strict"; let message = "Hello world"; console.log(message); */ //-- Primitives let isPresent: boolean = true; let beast: number = 66.4566; let anotherNumber: string = beast.toPrecision(1); let notDefined: undefined = undefined; let notPresent: null = null; let penta: symbol = Symbol('star'); let biggy: bigint = 9007199254740991n; //-- List classes let array: Array<number> = [1, 2, 3]; let array2: number[] = [1, 2, 3]; let set: Set<number> = new Set([1, 2, 3]); //-- Generics class Queue<T> { private data: Array<T> = []; push(item: T) { this.data.push(item); } pop(): T | undefined { return this.data.shift(); } } let queue: Queue<number> = new Queue(); queue.push(33); //-- Tuples let tuple: [number, number] = [0, 0]; // [0, 0, 0] is an error //-- Types and type alias let center: {x: number, y: number} = { x: 0, y: 0 }; type Point = {x: number, y: number}; let center2: Point = {x: 0, y: 0}; //-- Functions function add(a: number, b: number): number { return a + b; } function log(message: string): void { console.log(message); } function sum(...values: number[]) {} let addFunction: (a: number, b: number) => number; addFunction = add; //-- Structural typing type User = { id: string }; type Product = { id: string }; let user: User = { id: "1" }; let product: Product = { id: "3" } ; let user2: User = product; // No error type Point2D = { x: number, y: number }; type Point3D = { x: number, y: number, z: number}; let p2: Point2D = { x: 0, y: 0 }; let p3: Point3D = { x: 1, y: 1, z: 1 }; p2 = p3; // No error. p3 = p2 <- error function takePoint2D (point: Point2D) {} takePoint2D(p3); //-- Classes class Animal { private name: string; protected position: number; #last_name: string; // This is also private, but `private` is prefered constructor(name: string) { this.name = name; this.position = 0; this.#last_name = "Toby"; } public move(meters: number): void { this.position += meters; } } class Bird extends Animal { // Having private position would not work fly (miles: number): void { this.position += (miles * 0.0006); } } let cat = new Animal("cat"); cat.move(10); //-- Any and Unknown let exampleAny: any = 333; let exampleUnknown: unknown = 333; // safer exampleAny.allow.anything(); let bBoolean: boolean = exampleAny; // exampleUnknown.does.not.allow(); // exampleUnknown.trim(); // does not allow this either if (typeof exampleUnknown == 'string') { exampleUnknown.trim() } //-- Type assertion and casts /* test.js contains: export function load() { if (3 % 2 == 0) return "caca"; else if (3 % 65 == 0) return AnonymousObject(); return 33; } */ import { load } from "./test.js" // we require to "allowJS" in tsconfig.json let varLoaded = load(); const trimmed: string = (varLoaded as string); // assertion let strVariable: string = "1992"; let numVariable: number = +strVariable; // cast let bVariable: boolean = (numVariable == 1992); /* When you want to use an external variable, like process.env from node. We will do a declaration: declare const process: any; There is a way to directly define those external variables in a non invasive way: - You can set them in `env.d.ts` file. But if you are going to use node typing, the best way is to install the `@types/node` package. With this, it is not required to import those variables. For example, with Express.js: `@types/express` However, for using TypeScript on node we will make use of ts-node package. JS: If you create this class: class Person { growOld() { this.whatever } } And you use a person instance with: const growOld = person.growOld(), `this` is lost. For maintaining the `this` context growOld must be defined like this: class Person { growOld = () => { this.whatever } } */ //-- Readonly type XValue = { readonly x: number; } let xvalue: XValue = {x: 33} // xvalue.x = 33; this gives an error //-- Unions function format(input: string | string[]) : void {} // Accepts string or string array //-- Literals let direction: 'North' | "South" | "East" | "West"; direction = 'North'; // direction = 'north'; error class Cat { meow() {}} class Dog { bark() {}} type Pet = Cat | Dog; let pet: Pet = new Cat() if (pet instanceof Cat) pet.meow(); //-- Discrimination type Circle = { kind: 'circle', //This can be a boolean. The real example is with isValid: true, and then isValid: False validatedValue: string, }; type Square = { kind: 'rectangle', errorReason: string, }; type Figure = | Circle | Square; function logResult(result: Figure) { if (result.kind == 'circle') { console.log('Success, validated value:', result.validatedValue); } if (result.kind == 'rectangle') { console.error('Failure, error reason:', result.errorReason); } } //-- parameter properties class Person { constructor (public name: string, public age: number) {} } const adam = new Person('Adam', 12000); console.log(adam.name); // Prints Adam //-- Intersection types type Another2D = { x: number, y: number } type Another3D = Another2D & { z: number } let aP3D = {x: 2, y: 3, z: 4} type AnotherPerson = { name: string } type Email = { email: string} function contact(data: AnotherPerson & Email) {} contact({name: "hola", email: "a@b.com"}) type ContactData = AnotherPerson & Email; function contact2(data: ContactData) {} //-- Optionals type APerson = {name: string, email: string, phone?: string }; const alfred: APerson = {name: "alfred", email: "a@a.com"} //-- Not null type Position = {x: number, y?: number | null | undefined} let p: Position = {x: 33} console.log(p.y!) //-- Interfaces interface Location2D { x: number, y: number } interface Location2D { // this is merged xy: string } interface Location3D extends Location2D { z: number } let location: Location3D = {x: 0, y: 0, z: 0, xy: "00"} //-- never let example: never; // ---- type Square1 = { kind: 'square', size: number, }; type Rectangle1 = { kind: 'rectangle', width: number, height: number, }; type Circle1 = { kind: 'circle', radius: number, }; type Shape1 = | Square1 | Rectangle1 | Circle1; function area(s: Shape1) { if (s.kind === 'square') { return s.size * s.size; } else if (s.kind === 'rectangle') { return s.width * s.height; } else if (s.kind === 'circle') { return Math.PI * (s.radius ** 2); } const _ensureAllCasesAreHandled: never = s; // this is an assert. It will raise a TS error if we do not handle a type return _ensureAllCasesAreHandled; }
type Animal = { name: string, voice(): string } // class Cat implements Animal {} <- it will fail until implement name and voice //-- Assigment assertion let dice!: number; // Allows to indicate it could not be a value assigned to it when used console.log(dice); // It does not raises error // class Point {x: number} // This gives an error as nothing assigns x. You can define it as x! //-- Type guards type Square = { size: number, }; type Rectangle = { width: number, height: number, }; type Shape = Square | Rectangle; // shape is square indicates Typscript that when this is true, shape is Square and will work like that function isSquare(shape: Shape): shape is Square { return 'size' in shape; } function isRectangle(shape: Shape): shape is Rectangle { return 'width' in shape; } function area(shape: Shape) { if (isSquare(shape)) { return shape.size * shape.size; } if (isRectangle(shape)) { return shape.width * shape.height; } const _ensure: never = shape; return _ensure; } //-- Function assert function assert(condition: unknown, message: string): asserts condition { if (!condition) throw new Error(message); } function assertDate(value: unknown): asserts value is Date { let isDate: boolean = value instanceof Date; if (!isDate) throw new TypeError("Value is not date") } // assert(maybePerson != null, "Could not load a person") // assertDate(maybePerson.dateOfBirth); // maybePerson.dateOfBirth.toISOString(); // this would give an error if the previous assert was not done //-- Function overloading // We define which value returns a function depending on its input function makeDate(timestamp: number): Date; function makeDate(year: number, month: number, day: number): Date; function makeDate(timestampOrYear: number, month?: number, day?: number): Date { if (month != null && day != null) { return new Date(timestampOrYear, month - 1, day); } else { return new Date(timestampOrYear); } } const doomsday = makeDate(2000, 1, 1); // 1 Jan 2000 const epoch = makeDate(0); // 1 Jun 1970 // Another way of function overloading type Add = { (a: number, b: number): number, (a: number, b: number, c: number): number, debugName?: string } const add: Add = (a: number, b: number, c?: number) => { return a + b + (c != null ? c : 0); } add.debugName = 'Addition!' //-- Abstract classes abstract class Command { abstract commandLine(): string } class GitReset extends Command { commandLine(): string { return "reset" } } //-- Typing dictionaries type Dictionary = { [key: string]: boolean } type CommandDictionary = { [name: string]: Command } const commands: CommandDictionary = { rset: new GitReset() } delete commands["rset"]; commands["reset"] = new GitReset(); commands.resetig = new GitReset(); //-- Inmutable function reverseSorted(input: readonly number[]): number[] { // return input.sort(); is error as it mutates the array return input.slice().sort(); // slice copy the input } type Point = readonly [number, number]; // Tuple not mutable const dave = { name: "Dave" } as const; // dave.name = "Juan" Esto da error por el "as const" que pone fields como readonly //-- This // function double () { return this.value * 2; } // const valid = { value: 10, double, } // valid.double(); // Estas últimas añaden la double function a valid, pero no se detecta el tipo de valor // La siguiente es mejor function double(this: {value: number }) { return this.value * 2; } //-- Constraints for generics type NameFields = { firstName: string, lastName: string }; // El tipo T ha de cumplir Namefields y el retorno tendrá fullName function addFullName<T extends NameFields>(obj: T): T & { fullName: string } { return { ...obj, fullName: `${obj.firstName} ${obj.lastName}`, }; } const john = addFullName({ email: 'john@example.com', firstName: 'John', lastName: 'Doe' }); console.log(john.email); // john@example.com console.log(john.fullName); // John Doe const jane = addFullName({ firstName: 'Jane', lastName: 'Austen' }); //-- Type Generation & Aliases const center = {x: 0, y: 0, z: 0} type AnotherPoint = typeof center; const unit: typeof center = {x: 1, y: 1, z: 1} // Esto puede ser usado para generar un tipo de lo que devuelve un REST // Con lo siguiente, indicamos el anidado tipo type RESTResponse = { payment: { creditCardToken: string } } function getPayment(): RESTResponse['payment'] { return { creditCardToken: "333333aaa333" } } //-- Get values of keys type AnimalKeys = keyof Animal; function logAnimal(obj: any, key: AnimalKeys) { return obj[key]; // We know key can only be name or voice } // With this we limit keys from obj and from values knowing it is good function setAnimal<Obj, Key extends keyof Obj>(obj: Obj, key: Key, value: Obj[Key]) { obj[key] = value; } //-- Conditional type type IsNumber<T> = T extends number ? 'number' : 'other'; type WithNumber = IsNumber<number>; type WithOther = IsNumber<string>; export type TypeName<T> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : 'other'; function typeName<T>(t: T): TypeName<T> { return typeof t as TypeName<T>; } function test () {} const a = typeName(1); // number const b = typeName("s"); // string const c = typeName(test); // other //-- Generate type with ReturnType function createPerson(name: string) { return { name: name } } function logPerson(person: ReturnType<typeof createPerson>) { console.log(person.name) } //-- Mapped types type SuperPoint = { w: number, x: number, y: number, z: number } // Type that creates readonly by making a loop of Item (loop variable) // in all keys of given type T returning the type of T[Item] and setting it // to readonly (Readonly type already exists) type MyReadonly<T> = { readonly [Item in keyof T]: T[Item] } const SuperCenter: MyReadonly<SuperPoint> = { w: 0, x: 0, y: 0, z: 0 } //-- Type modifiers // with + you can add modifiers, with - remove them type OneType = { readonly name: string } type WithoutReadonly<T> = { -readonly [P in keyof T]: T[P] } // You could make it even optional with +? type OneWoRO = WithoutReadonly<OneType>; //-- Already defined modifiers // Partial<T> for allowing not all values // Required<T> for requiring all the values of a type // Readonly<T> // Record<K,V> //-- Template literal types let str: string; str = "whatever"; let strLiteral: 'hello'; strLiteral = "hello"; // strLiteral = "this is error" let templateLiteral: `Example: ${string}`; templateLiteral = "Example: hello"; templateLiteral = "Example: hello this is good" // templateLiteral = "this is an error Example: ok!" type CSSValue = number | `${number}px` | `${number}rem`; type Size = 'small' | 'large'; type Color = 'primary' | 'secondary'; type Style = `${Size}-${Color}`; function applyStyle(style: Style) {} applyStyle("small-primary"); //applyStyle("smll-primary");