Herramientas de usuario

Herramientas del sitio


wiki2:typescript_review

TypeScript Review

/*
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");
wiki2/typescript_review.txt · Última modificación: 2022/11/02 16:17 (editor externo)