【TS】加深TS理解的开发实战示例代码

TS接口

开发实战:基于类类型接口设计实现日期时间应用

interface ITimeDate {
    curTime: Date;
    setTime(cur: Date): void;
    getTime(): Date;
}

class CTime implements ITimeDate {
    curTime: Date;
    constructor(cur: Date) {
        this.curTime = cur;
    }

    setTime(cur: Date): void {
        this.curTime = cur;
    }

    getTime(): Date {
        let curTime: Date;
        if(this.curTime) {
            curTime = this.curTime;
        } else {
            curTime = new Date();
        }
        return curTime;
    }
}

let ct: CTime = new CTime(new Date());
console.log(ct.getTime());

let newTime: Date = new Date("2021/10/21 00:00:00");
ct.setTime(newTime);
console.log("New time is: " + ct.getTime());

开发实战:基于单接口继承设计实现计算周长的应用

interface IShape {
  girth: number;
}
interface ISqure extends IShape {
  shapeType: string;
  getGirth(): number;
}

class CSquare implements ISqure {
  shapeType: string;
  girth: number;

  constructor(sideLength: number) {
    this.shapeType = 'Square';
    this.girth = sideLength * 4;
  }

  getGirth(): number {
    return this.girth;
  }
}

let cs: CSquare = new CSquare(6);
console.log(
  cs.shapeType + 'sideLength is 6, then girth is ' + cs.getGirth() + '.'
);

声明了一个表示几何图形的抽象接口IShape,其中包括一个表示图形周长的属性girth.

同时声明了一个继承自抽象接口IShape的接口ISquare,用来描述正方形。

最后定义了一个实现接口ISquare的类CSquare。

开发实战:基于多接口继承设计实现计算周长和面积的应用

前一个应用是通过单接口继承实现的,其实TypeScript多接口继承功能更常用。

我们对上述示例进行改进,通过TypeScript多接口继承设计实现同时计算正方形周长和面积的应用。

interface IGirth {
    girth: number;
}
interface IArea {
    area: number;
}

interface ISquareB extends IGirth, IArea {
    shapeType: string;
    getGirth(): number;
    getArea(): number;
}
class CSquareB implements ISquareB {
    shapeType: string;
    girth: number;
    area: number;
    constructor(sideLength: number) {
        this.shapeType = "Square";
        this.girth = 4 * sideLength;
        this.area = sideLength * sideLength;
    }
    getGirth(): number {
        return this.girth;
    }
    getArea(): number {
        return this.area;
    }
}

let csB: CSquareB = new CSquareB(6);
console.log(csB.shapeType + " sideLength is 6, then girth is " + csB.getGirth() + ".");
console.log(csB.shapeType + " sideLength is 6, then area is " + csB.getArea() + "." );

开发实战:基于混合类型接口设计实现计数器应用

下面通过TypeScript混合类型接口设计实现一个具有计数器(初始化、累加、调整步长和重置)功能的应用。

interface ICounter {
    (s: string): void;
    current: number;
    interval: number;
    count(): void;
    setInterval(i: number): void;
    reset(): void;
}

function getCounter(): ICounter {
    let counter = <ICounter>function(s: string): void {
        console.log(s);
    }

    // init counter
    counter.current = 0;
    counter.interval = 1;

    counter.count = function() {
        counter.current += counter.interval;
        console.log('Now current count is: ' + counter.current + '.');
    }

    counter.setInterval = function(i: number) {
        counter.interval = i;
        console.log('Now interval changes to:' + counter.interval + '.');
    }

    counter.reset = function() {
        counter.current = 0;
        console.log('Now current count is reset to 0.');
    }

    return counter;
}

let c = getCounter();
console.log('counter typescript app:');
c.count();
c.setInterval(5);
c.count();
c.reset();
c.setInterval(1);
c.count();

最终结果:

counter typescript app:
Now current count is: 1.
Now interval changes to:5.
Now current count is: 6.
Now current count is reset to 0.
Now interval changes to:1.

ICounter 接口定义了一个函数类型和一些属性和方法:

interface ICounter {
    (s: string): void; // 这是一个函数类型
    current: number;   // 这是一个属性
    interval: number;  // 这是一个属性
    count(): void;     // 这是一个方法
    setInterval(i: number): void; // 这是一个方法
    reset(): void;     // 这是一个方法
}

通过类型断言 ,我们确保 counter 变量不仅包含一个函数,还包含 current、interval 属性以及 count、setInterval 和 reset 方法。这样,TypeScript 编译器就会检查这些属性和方法是否都被正确地初始化和实现。

说明如下代码意思:

let counter = <ICounter>function(s: string): void {
    console.log(s);
}
  • 定义一个匿名函数 function(s: string): void { console.log(s); }。
  • 使用类型断言 将这个匿名函数断言为 ICounter 类型。

在 TypeScript 中,类型断言有两种语法形式:

  • 使用尖括号 ,例如
  • 使用 as 关键字,例如 as ICounter。

TypeScript类

开发实战:设计实现存取器应用

在TypeScript类语法中,支持通过getters/setters存取器方式来执行对类成员的访问操作,这种方式能帮助开发人员有效地控制对类成员的访问。在TypeScript类中,设计存取器需要使用get(获取)和set(存储)修饰符来定义。

class CGetSet {
    private _name: string;

    constructor(theName: string) {
        this._name = theName;
    }

    get Name(): string {
        return this._name;
    }

    set Name(theName: string) {
        theName = theName.trim();
        if(theName && theName.length) {
            this._name = theName;
        } else {
            console.log('error:theName is empty.');
        }
    } 
}

let gs = new CGetSet('set&get');
console.log(gs.Name); // set&get
gs.Name = 'reset set&get';
console.log(gs.Name); // reset set&get

TypeScript 函数

开发实战:箭头函数与this关键字应用

let userInfo = {
    uname: 'zhangsan',
    printInfo() {
        return function () {
            return {name: this.uname}
        }
    }
}

const ui = userInfo.printInfo();
console.log(ui().name)

上面代码中this会报错,编译信息提示this关键字的类型为Any的错误。

方式一,用箭头函数调整:

let userInfo = {
    uname: 'zhangsan',
    printInfo() {
        return () => {
            return {name: this.uname}
        }
    }
}

const ui = userInfo.printInfo();
console.log(ui().name)

方式二,使用 that 或 self 变量:

let userInfo = {
    uname: 'zhangsan',
    printInfo() {
        const self = this;
        return function () {
            return {name: self.uname}
        }
    }
}

方式三:使用 apply 或 call 方法

可以在调用嵌套函数时使用 apply 或 call 方法来显式设置 this。

let userInfo = {
    uname: 'zhangsan',
    printInfo() {
        return function (this: typeof userInfo) {
            return {name: this.uname}
        }
    }
}

const ui = userInfo.printInfo();
console.log(ui.apply(userInfo).name)

TypeScript 泛型

开发实战:泛型变量应用

function echoVar<T>(arg: Array<T>): Array<T> {
    console.log('arg length:' + arg.length)
    return arg;
}

console.log(echoVar([1, 2]))
console.log(echoVar(['a', 'b']))

上述代码定义了一个泛型函数echoVar,参数arg定义为类型<T>的数组形式Array<T>。

开发实战:泛型类型应用

使用泛型类型定义泛型函数的应用

function echo_type<T>(arg: T): T {
    return arg;
}

let arrow_echo_type: <T>(arg:T) => T = echo_type;

console.log(arrow_echo_type("Hello")); // Hello

上述代码,通过泛型类型<T>(arg:T)定义了一个泛型函数变量arrow_echo_type,并指定为泛型函数echo_type。

另外,我们可以使用具有调用签名的对象字面量来定义泛型函数:

function echo_type<T>(arg: T): T {
    return arg;
}

let liter_echo_type: {<T>(arg:T):T} = echo_type;
console.log(liter_echo_type("Hello")); // Hello

开发实战:泛型接口应用

我们可以将对象字面量“{<T>(arg:T):T}”单独封装为一个接口形式。

interface IGenericsEcho {
    <T>(arg: T): T;
}

function fn_echo_generics<T>(arg: T): T {
    return arg;
}

let echo_gen: IGenericsEcho = fn_echo_generics;
console.log(echo_gen('define generics func by interface'));

上述代码,声明了一个接口IGenericsEcho,通过对象字面量“<T>(arg:T):T”定义了泛型函数的类型。

通过泛型接口IGenericsEcho和泛型函数fn_echo_generics定义了一个函数变量echo_gen。

进一步在接口声明中直接加上类型<T>,写成真正的泛型接口形式。上述代码调整为如下:

interface IGenericsEcho<T> {
   (arg: T): T;
}

function fn_echo_generics<T>(arg: T): T {
    return arg;
}

let echo_gen_str: IGenericsEcho<string> = fn_echo_generics;
let echo_gen_num: IGenericsEcho<number> = fn_echo_generics;
console.log(echo_gen_str('define generics func by interface'));
console.log(echo_gen_num(123));

上面代码声明了一个泛型接口IGenericsEcho<T>,接口类型为“(arg:T):T”。

我们在使用该泛型接口定义函数变量echo_gen_num和echo_gen_str时,需要指定具体类型。

开发实战:泛型类应用

class EchoGenerics<T> {
    private _m_arg: T;
    constructor(arg: T) {
        this._m_arg = arg;
    }

    public echo(): void {
       console.log(this._m_arg)
    }
}

let g_echo_str = new EchoGenerics<string>("Hello World");
g_echo_str.echo();
let g_echo_num = new EchoGenerics<number>(123);
g_echo_num.echo();

TypeScript 高级类型

类型别名

在类型别名中使用泛型来模拟接口功能的应用。

type StrNum = string | number;
type aliasGeneric<T> = {
    x: T;
    y: T;
}

function getXY(ag: aliasGeneric<StrNum>) {
    if(typeof ag.x === 'number' && typeof ag.y === 'number') {
        console.log(ag.x + ag.y)
    }
    else if(typeof ag.x === 'string' && typeof ag.y === 'string') {
        console.log(`${ag.x} ${ag.y}`)
    }
    else {
        console.log('other alias-type generic')
    }
}

let agStr: aliasGeneric<string> = {
    x: 'a',
    y: 'b'
}
getXY(agStr);

let agNum: aliasGeneric<number> = {
    x: 1,
    y: 2
}
getXY(agNum);

映射类型

如下示例,通过映射类型方式将原接口IPerson的属性转换为只读属性:

interface IPerson {
    name: string;
    age: number;
}

type TReadyOnly<T> = {
    readonly [p in keyof T]: T[p];
}

type ReadyOnlyPerson = TReadyOnly<IPerson>;

通过映射类型方式将原接口IPerson的属性转换为可选属性:

interface IPerson {
    name: string;
    age: number;
}

type TOptional<T> = {
    [p in keyof T]?: T[p];
}

type OptionalPerson = TOptional<IPerson>;
posted @ 2024-09-22 21:36  风雨后见彩虹  阅读(19)  评论(0编辑  收藏  举报