TypeScript core foundation

Posted by grayscale2005. on Thu, 23 Sep 2021 03:06:10 +0200

Configure VScode to automatically generate TS files

  • Generate ts configuration file
tsc --init
  • Activate output path

  • When the configuration is saved, it is output to the specified path

  1. Click Run task under the terminal
  2. Click typescript
  3. Click tsc monitor

Basic data types in TS

boolean type

let flag: boolean = true;
flag = false;

number type

let num: number = 666;
num = 777;

string type

let str: string = '666';
str = '777';

array type

// Mode 1
let arr: number[] = [1,2,3];
// Mode 2
let arr2: Array<number> = [6,7,8];

Tuple type

// Tuple type
let arr: [number,string,boolean] = [1,'60',true];

Enumeration type

// Enumeration type
enum Flag{color1 = 'blue',color2 = 'pink'};

let flag: Flag = Flag.color2;

console.log(flag);

Any type of any

// Any type
let num: any = 666;
num = 'test';
num = true;

undefined type

// undefined type
let num: undefined;
console.log(num);

null type

let num: null;
num = null;

void type

  • Indicates that the method does not have any return type
function run(): void{
    console.log('run');
}

never type

Represents a function that cannot reach the end point, such as an endless loop or throwing an exception.

function test(): never {
    throw new Error('error');
}

Function type of TS

basic form

function test(name: string,age: number): string{
    return `${name} ---- ${age}`
}
test('nihao',666);

Optional parameters

In JS, the parameters defined by the function can be passed or not, but they must be passed in TS. if they are not passed, optional parameters need to be configured.

function test(name: string,age?: number): string{
    return `${name} ---- ${age}`
}
test('nihao');

Default parameters

number in the following example is the default parameter.

function test(name: string,age: number=20): string{
    return `${name} ---- ${age}`
}
test('nihao');

Remaining parameters

function sum(...arg: number[]): number{
    return arg.reduce((pre,cur) => pre + cur,0)
}
sum(1,2,3,4)

function overloading

In JS, once a method with the same name is in the same scope, the following will overwrite the above, but in TS, it will be overloaded.

  • Note: the following any does not mean that any type can be passed.
function test(name: string): string;

function test(age: number): string;
// In fact, any here is not a real any. One of the above two types must be passed. Otherwise, an error is reported
function test(str: any): string{
    if (typeof str === 'string') {
        return 'full name' + str;
    } else {
        return 'Age' + str;
    }
}
test('Reese');

Classes in TS

Definition of properties and methods in class

class Person{
    name: string;
    constructor(n: string) {
        this.name = n;
    }   
    run() :void {
        console.log(this.name);
    }
}

const p = new Person('Zhang San');
p.run();

Implementing inheritance in TS

The extension and super keywords are mainly used to implement inheritance in TS.

class Person{
    name: string;

    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return `${this.name}I'm learning`
    }
}

class child extends Person{
    constructor(name: string) {
        super(name);
    }
}

const c = new child('Xiao Ming');
console.log(c.run());

Modifier in class

  • public: it can be called by itself, subclasses and instances
class Person{
    public name: string;

    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return `${this.name}I'm learning`
    }
}

const p = new Person('Zhang San');
console.log(p.name);
  • protected: it can be called by itself and subclasses
class Person{
    protected name: string;

    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return `${this.name}I'm learning`
    }
}

class Child extends Person{
    
    constructor(name: string) {
        super(name)
    }
    run2(): void{
        console.log(this.name);
    }
}

const c = new Child('666');
console.log(c.run());
  • private: it can only be accessed by itself
class Person{
    private name: string;

    constructor(name: string) {
        this.name = name;
    }
    run(): string {
        return `${this.name}I'm learning`
    }
}

const p = new Person('Zhang San');
p.name   // report errors

Note: if the class member modifier is not written, the default is public

Static properties and static methods

  • Define static attributes through static keyword
class Person{
    name: string;
    constructor(name: string){
        this.name = name;
    }

    run() {
        console.log(this.name);
    }
    static work() {
        console.log('This is the static method of the class');
    }
}

const p = new Person('Zhang San');

// p.work() / / an error is reported
Person.work();
  • Static methods can only call static properties, not instance properties
class Person{
    name: string;
    static age: number = 18;
    constructor(name: string){
        this.name = name;
    }

    run() {
        console.log(this.name);
    }
    static work() {
        console.log('This is the static method of the class');
        console.log('Age is:',Person.age);        
    }
}

const p = new Person('Zhang San');

Person.age;
Person.work();

polymorphic

Polymorphism means that the parent class defines a method not to be implemented, but let its subclasses implement it. Each subclass has different performance.

class Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat(): void {
        console.log('This is the parent class eat');
    }
}

class Cat extends Animal {
    name: string;
    constructor(name: string) {
        super(name)
        this.name = name;
    }
    eat(): void {
        console.log('This is a polymorphism of subclasses:',this.name);
        
    }
}
const c = new Cat('kitten');
c.eat();

abstract class

Abstract keyword is used to define abstract classes and abstract methods. Abstract methods in abstract classes do not contain specific implementations and must be implemented in derived classes. Abstract methods can only be placed in abstract classes.

  1. Abstract classes cannot be instantiated concretely
abstract class Animal{
    abstract eat(): void;
}
const a = new Animal()   // Direct error reporting
  1. Subclasses of an abstract class must implement abstract methods in the abstract class
abstract class Animal {
    abstract eat(): void;
}
class cat extends Animal {
    eat(): void {
        console.log(666);
    }
}
const c = new cat();
c.eat()

Interface in TS

Interfaces also exist to define standards.

Attribute interface

interface fullname {
    firstName: string;
    secondName: string;
}
function getName (name: fullname) {
    console.log(`${name.firstName} + '---' + ${name.secondName}`);
}
const obj = {
    firstName: 'Small',
    secondName: 'bright'
}
getName(obj);

Optional properties of the interface

interface fullname {
    firstName: string;
    secondName?: string;
}
function getName (name: fullname) {
    console.log(`${name.firstName} + '---' + ${name.secondName}`);
}
const obj = {
    firstName: 'Small',
    // secondName: 'Ming'
}
getName(obj);

Interface of function type

interface encrypht {
    (key: string,value: string): string;
}
const md5: encrypht = function md5(key: string, value: string): string {
    return key + value;
}
console.log(md5('1','2'));

Indexable interface

This interface is mainly used to constrain arrays and objects.

  • Constraints on arrays
interface useArr {
    [index: number]: string;
}

const arr: useArr = ['1','2']
  • Constraints on objects
interface useObj {
    [index: string]: string;
}
const obj: useObj = {
    name: 'Zhang San',
    age: '666'
}

Class type interface

The significance of class type interface is mainly to constrain classes.

interface Animal {
    name: string;
    eat(str: string): void;
}
class Cat implements Animal {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat(str: string): void {
        console.log(this.name + str);
    }
}
const cat = new Cat('little cat');
console.log(cat.eat('Corn'));

Interface expansion

Interface extension means that an interface can inherit another interface.

interface Animal {
    eat(): void;
}
interface Person extends Animal {
    work(): void;
}
class people implements Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    eat() {
        console.log('eat');
    }
    work() {
        console.log('work');
    }
}
const p = new people('Xiao Zhang');
p.eat()
p.work()

Inheritance + implementation

interface Animal {
    eat(): void;
}
interface Person extends Animal {
    work(): void;
}
class Programer {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    coding(): void {
        console.log(this.name + 'Writing code');
    }
}
class people extends Programer implements Person {
    name: string;
    constructor(name: string) {
        super(name);
        this.name = name;
    }
    eat() {
        console.log('eat');
    }
    work() {
        console.log('work');
    }
}
const p = new people('Xiao Zhang');
p.coding();

Generics in TS

Generics can support unspecified types.

Generic definition

function getData<T>(value: T): T {
    return value;
}

console.log(getData<string>('123'));
console.log(getData<number>(123));

Generic type of class

class MinClass<T> {
    list: T[] = [];

    add(num: T) {
        this.list.push(num);
    }

    min(): T {
        let minNum = this.list[0];
        for (let v of this.list) {
            if (v < minNum) {
                minNum = v;
            }
        }
        return minNum;
    }
}
const m = new MinClass<number>();

generic interface

  • Writing method 1
interface configFn {
    <T>(value: T): T;
}

const fn: configFn = function<T>(value: T): T {

    return value;
}

console.log(fn<string>('666'));
  • Writing method 2
interface configFn<T> {
    (value: T): T;
}

function getData<T>(value: T): T {
    return value;
}

const myGetData: configFn<string> = getData;

myGetData('20')

Generic classes implement generic interfaces (for logical reuse)

Method for quickly implementing generic interface by generic class

  1. Define the initial structure
class MongoDb<T> implements DBI<T> {
    
}
  1. Hover the cursor and click quick fix

  2. Click to implement the interface

  • Classic examples
interface DBI<T> {
    add(info: T): boolean;
    update(info: T): boolean;
    delete(id: number): boolean;
    get(id: number): any[];
}

/**
 * @description: The class that implements the generic interface must also be a generic class
 * @param {*}
 * @return {*}
 */
class MysqlDb<T> implements DBI<T> {
    add(info: T): boolean {
        throw new Error("Method not implemented.");
    }
    update(info: T): boolean {
        throw new Error("Method not implemented.");
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.");
    }
    get(id: number): any[] {
        throw new Error("Method not implemented.");
    }
}

class MongoDb<T> implements DBI<T> {
    add(info: T): boolean {
        console.log(info);
        return true;
        
    }
    update(info: T): boolean {
        throw new Error("Method not implemented.");
    }
    delete(id: number): boolean {
        throw new Error("Method not implemented.");
    }
    get(id: number): any[] {
        throw new Error("Method not implemented.");
    }
    
}

class User {
    username: string | undefined;
    password: string | undefined;   
}

const u = new User();
u.username = 'Zhang San';
u.password = '123';

const m = new MongoDb();
m.add(u);

Namespace

  • Difference between namespace and module

Namespace: an internal module, which is mainly used to organize code and avoid naming conflicts.
Module: the abbreviation of the external module of TS, focusing on code reuse. There may be multiple namespaces in a module.

namespace A {
    export class Animal {
        name: string | undefined;
        age: number | undefined;
    }
}
namespace B {
    export class Animal {
        name: string | undefined;
        age: number | undefined;
    }
}
const cat = new A.Animal()
const dog = new B.Animal()

TS trimmer

What's the use of decorators?

Decorator is a special type of declaration that can be attached to class declarations, methods, properties or parameters to modify the behavior of a class. Generally speaking, decorator is a method that can be injected into classes, methods and attribute parameters to expand the functions of classes, attributes, methods and parameters.

Class decorator

Note: the implementation of the class decorator function requires the following code to be configured in the tsconfig.json file.

"experimentalDecorators": true
  1. Ordinary decorator (unable to pass reference)
function logClass(params: any) {
    // params here refers to the function below the decorator
    console.log(params);
    // Here are the properties of our dynamic expansion
    params.prototype.apiUrl = 'http://www.baidu.com'
}

// 
@logClass
class HttpClient {
    constructor() {

    }
    getData() {

    }
}

const h: any = new HttpClient();
console.log(h.apiUrl);
  1. Decorator factory (can be referred to)
// Decorator factory
function logClass(params: any) {
    // params: it is a parameter passed from the decorator
    // The following target refers to the class itself
    return function(target: any) {
        console.log('This is target: ',target);
        console.log('This is params: ',params);
        target.prototype.apiUrl = params;
    }
}

// 
@logClass('666')
class HttpClient {
    constructor() {

    }
    getData() {

    }
}
const h: any = new HttpClient();
console.log(h.apiUrl);  //666

Class decorator overload

function logClass(target: any) {
    return class extends target {
        apiUrl: string = 'This is a modified one url';
        getData() {
            this.apiUrl = 'This is a modified one url---'
            console.log(this.apiUrl);
            
        }
    }
}
@logClass
class HttpClient {
    apiUrl: string | undefined;
    constructor() {
        this.apiUrl = 'This is in the constructor URL'
    }
    getData() {
        console.log(this.apiUrl);
    }
}
const h = new HttpClient();
h.getData();

Attribute decorator

The property decorator expression will be called as a function at runtime, passing in the following two parameters

  1. For static members, it is the constructor of the class, and for instance members, it is the prototype object of the class.
  2. Name of the member.
// Attribute decorator
function logProperty(params: any) {
    return function(target: any,attr: any) {
        // The target here refers to the prototype object of the class
        // attr here refers to the attributes of the class
        console.log(target);
        console.log(attr);
        target[attr] = params;
    }
}
class HttpClient {
    @logProperty('http://www.baidu.com')
    url: string | undefined;
    constructor() {

    }
    getData() {
        console.log(this.url);
    }
}
const h = new HttpClient();
h.getData()

Method decorator

In the method decorator, you can modify the method or property corresponding to the method decorator.

/**
 * @description: Method decorator
 * @param {any} params
 * @return {*}
 */
function logFun(params: any) {
    /**
     * @description: 
     * @param {any} target: Prototype object
     * @param {any} methodName: Method name
     * @param {any} desc: Property description of the method
     * @return {*}
     */
    return function(target: any,methodName: any,desc: any) {
        console.log(target);
        console.log(methodName);
        console.log(desc.value);

        const oldMethod = desc.value;
        desc.value = function(...args: any[]) {
            args = args.map(item => {
                return String(item)
            })
            oldMethod.apply(this,args)
        }
    }
}
class HttpClient {
    url: string | undefined;
    constructor() {
    }
    @logFun('Hello')
    getData(...args: any[]) {
        console.log(args);  // ['123','666']
        console.log('This is getData Methods in');
    }
}
const h = new HttpClient();
h.getData(123,'666')

Method parameter decorator

/**
 * @description: Method parameter decorator
 * @param {any} params
 * @return {*}
 */
function logParams(params: any) {
    /**
     * @description: 
     * @param {any} target: Prototype object of class
     * @param {any} methodName: Method name corresponding to parameter
     * @param {any} paramsIndex: Index corresponding to parameter
     * @return {*}
     */
    return function(target: any,methodName: any,paramsIndex: any) {
        console.log(params);
        console.log(target);
        console.log(methodName);
        console.log(paramsIndex);
        // Add an attribute
        target.apiUrl = params;
    }
}
class HttpClient {
    url: string | undefined;
    constructor() {
    }
    getData(@logParams('Hello') uuid: any) {
        console.log(uuid);
    }
}
const h: any = new HttpClient();
h.getData(123);
console.log(h.apiUrl);   //Hello

Execution sequence of trimmer

First look at the code below

function logClass1(params: any) {
    return function(target: any) {
        console.log('Class decorator 1');
    }
}
function logClass2(params: any) {
    return function(target: any) {
        console.log('Class decorator 2');
    }
}
function logAttribute() {
    return function(target: any,attr: any) {
        console.log('Attribute decorator');
    }
}
function logMethod() {
    return function(target: any,methodName: any,desc: any) {
        console.log('Method decorator');
    }
}
function logParams1() {
    return function (target: any,methodName: any,paramsIndex: any) {
        console.log('Method parameter decorator 1'); 
    }
}
function logParams2() {
    return function (target: any,methodName: any,paramsIndex: any) {
        console.log('Method parameter decorator 2'); 
    }
}

@logClass1('111')
@logClass2('2222')
class HttpClient {
    @logAttribute()
    url: string | undefined;
    constructor() {
    }
    @logMethod()
    getData() {
        return true
    }
    setDate(@logParams1() attr1: any,@logParams2() attr2: any) {
        return true;
    }
}

Note: the execution order within class is in code order.

Topics: Javascript Front-end TypeScript Visual Studio Code