TS几个罕见特性的应用

本文列举了 ts 的几种不常见用法,分别是:

  • 函数重载
  • 接口继承
  • 类的内容
    • 属性关键字
    • 存储器 getter setter
    • extends vs implements
    • 泛型类

函数重载 ( Function Overloading )

这种其实可以用可选参数 + 或者类型替代,但如果把所有函数返回类型都列出,会清晰点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Overloading:
interface Circle {
radius: number;
}

interface Rectangle {
width: number;
height: number;
}

// 这里定义不同的函数入参及返回类型
function calculateArea(shape: Circle): number;
function calculateArea(shape: Rectangle): number;

function calculateArea(shape: any) {
if ((<Circle>shape).radius !== undefined) {
return Math.PI * (<Circle>shape).radius ** 2;
} else if (
(<Rectangle>shape).width !== undefined &&
(<Rectangle>shape).height !== undefined
) {
return (<Rectangle>shape).width * (<Rectangle>shape).height;
} else {
throw new Error('Unsupported shape type');
}
}

const circleArea = calculateArea({ radius: 5 });
console.log(circleArea); // Output: 78.53981633974483
const rectangleArea = calculateArea({ width: 10, height: 5 });
console.log(rectangleArea); // Output: 50

接口继承 ( Interface Extend )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface Parent1 {
p1: string;
p2: number;
}

interface Parent2 {
p3: boolean;
}

interface Child extends Parent1, Parent2 {
p4: string | number;
}

const o: Child = {
p1: "",
p2: 0,
p3: false,
p4: "",
};

类 ( Class )

定义属性

在ts里,所有属性需要在 constructor 之前规定其属性,如以下示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Article {
// 规定属性类型:
title: string;
content: string;
// 这里有属性 a, 如果在.tsconfig里,设置了"strict": true, 那么不设置 a 为可选属性则会报错。
a?: string;
// tsconfig严格模式下,没有初始化的属性,必须设置属性为可选或给定默认值:
b = '';

constructor(title: string, content: string) {
this.title = title;
this.content = content;
}
}

const a = new Article('题目', '内容');

publicprivate, protected, static, readonly 关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Artical {
public title?: string;
// private 只能在 当前类内部 进行访问
private content?: string;
// protected 只能在 当前类内部 或 其子类内部 进行访问:
protected innerData?: string;
// static 设置给类本身,而不是设置给类实例,访问时应该:
// Artical.author
static author: string = 'Jone';
// 只读属性, 不能被修改:
readonly version?: string;

// 多个修饰符连用:
protected static readonly c: string = ''

constructor(title: string, content: string) {
this.title = title;
this.content = content;
}
}

getter setter 和私有值

  • 私有属性,不想被外部访问,命名一般用下划线区分
  • 一般一个私有值,对应一个 getter, setter 存储器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class User {
private _password: string = '';

get password(): string {
return '*'
}

set password(newPassword: string) {
this._password = newPassword
}
}

const u = new User();
// 直接访问存储器getter: ⭕️
console.log(u.password);
// ❌:
// console.log(u._password);

抽象类 abstract vs 实现接口implements interface

  • abstract 用于给子类继承用,不单独做实例化
  • implementsabstract 的区别是,implements 可以实现多个接口 interface, 而 extends 只能继承一个抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
abstract class Animal {
abstract name: string;
abstract voice(): void;
}

// 只能 extends 一个父类:
class Cat extends Animal {
name: string = 'Cat';
voice(): void {
console.log('miao')
}
}

const c1 = new Cat();

interface AnimalName {
name: string;
}
interface AnimalVoice {
voice(): void;
}

class Dog implements AnimalName, AnimalVoice {
name: string = 'Dog';
voice() {
console.log('wang')
}
}

const d1 = new Dog();

当然,他们还能同时使用,但 extends 必须用在 implements 之前:

1
2
3
4
5
6
class Dog extends Animal implements AnimalName, AnimalVoice  {
name: string = 'Dog';
voice() {
console.log('wang')
}
}

泛型类

泛型除了应用在函数,还可以放在类里应用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class TestGenric<T> {
value: T;
constructor(value: T) {
this.value = value;
}

processValue (input: T): T {
return input
}
}

const s = new TestGenric<string>('string');
s.processValue('string');

const n = new TestGenric<number>(123);
n.processValue(123);