【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; // 这是一个方法
}
通过类型断言
说明如下代码意思:
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>;