Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
|
wiki2:new_typescript [2022/11/02 12:14] alfred |
wiki2:new_typescript [2022/11/03 09:30] (actual) |
||
|---|---|---|---|
| Línea 1: | Línea 1: | ||
| ====== TypeScript ====== | ====== TypeScript ====== | ||
| - | <code javascript> | + | * {{ :wiki2:js:typescript:typescript-cheat-sheets.zip |Cheatsheets}}, from [[https://www.typescriptlang.org/cheatsheets]] |
| - | /* | + | |
| - | 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; | + | |
| - | } | + | |
| - | </code> | + | |
| - | + | ||
| - | <code javascript> | + | |
| - | 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"); | + | |
| - | </code> | + | |