11 - TS class的补充


# TS Class的补充



## 定义类和成员



```tsx
class Point {
x: number
y: number

constructor(x : number, y : number){
this.x = x
this.y = y
}

public add(p : Point) : Point {
return new Point(p.x + this.x, p.y + this.y)
}
}

const p = new Point(0, 0)
const newP = p.add(new Point(1, 1))
console.log(newP)

```



## 成员函数的重载





```tsx
class Point {
// ...

public add(x : number, y : number) : Point;
public add(p : Point) : Point;
public add(x : number | Point, y? : number) {
if(typeof x === 'number') {
return new Point(this.x + x , this.y + y!)
}
const p = x
return new Point(this.x + p.x, this.y + p.y)
}
}

const p = new Point(0, 0)
const newP = p.add(new Point(1, 1))
console.log(newP)
```

重载可以获得更好的书写提示体验。

## Getter / Setter



获取和设置变成两个方法,隐藏了内部具体的实现。

```tsx
class C {
_length = 0;
get length() {
return this._length;
}
set length(value) {
this._length = value;
}
}

```

不推荐这样的实现(小师叔个人风格建议),推荐:

```tsx
class C {
_length = 0;
getLength() {
return this._length;
}
setLength(value) {
this._length = value;
}
}

```

## 索引器

```tsx
class Arr {
[i : number] : T
}

const a = new Arr()
a[10] = 100
console.log(a[10]) // 100
```



## 继承



### 类型继承

```tsx
interface BasicAddress {
name?: string;
street: string;
city: string;
country: string;
postalCode: string;
}

interface AddressWithUnit extends BasicAddress {
unit: string;
}

```

### 类的继承

```tsx
class Animal {
move() {
console.log("Moving along!");
}
}

class Dog extends Animal {
woof(times: number) {
for (let i = 0; i < times; i++) {
console.log("woof!");
}
}
}
```

实战中尽量使用组合:

```tsx
interface Movable {
move() : void;
}

class Dog implements Movable {
move () {
console.log('move dog')
}
}
```

思考:实现接口和继承的区别是什么?

思考:为什么用Movable而不是Animal作为接口?

或者泛型:

```tsx
class Movable {
animal : T

move(){
console.log(`${animal.getName()} is moving`)
}
}

```



划重点:继承会加重耦合,组合和泛型不会。



## 成员可见域

- public
- protected
- private

`public` `private` `protected` 这3个和其他语言一样,具体的:

`public` 成员允许所有程序访问,如果不标识任何约束,那么默认就是`public` 。

```ts
class Point {
x : number
}
// x可以被任意程序访问
```

`private` 成员只允许在当前类中访问。

```tsx
class Point {
private x : number
constructor(){
this.x = x // 成立
}

getX(){
return this.x
}
}

const p = new Point()
console.log(p.x)
// Error : Property 'x' is private and only accessible within class 'Point'.ts(2341)
```

`protected` 允许在当前类和它的继承类中访问成员。

```tsx
class Animal {
private _name1;
protected _name2;
}

class Dog extends Animal{
getName1(){
return this._name1 // Error
}

getName2(){
return this._name2 // pass
}
}
```

## 静态成员

```tsx
class MyClass {
static x = 0;
static printX() {
console.log(MyClass.x);
}
}
console.log(MyClass.x);
MyClass.printX();
```

静态成员和类的实例没有关系,静态成员、静态方法都绑定在类型本身上。



静态成员也会继承:

```tsx
class Base {
static getGreeting() {
return "Hello world";
}
}
class Derived extends Base {
myGreeting = Derived.getGreeting();
}
```

保留的特殊静态成员名称:

```tsx
class S {
static name = "S!";
// Error : Static property 'name' conflicts with built-in property 'Function.name' of constructor function 'S'.
}


```



### 为什么没有静态类?

在 Java中有静态类, 静态类是相对类的嵌套的概念,在JS虽然也存在类的嵌套,但是遵循B包的逻辑,比如:



```tsx
class A {
x : number
foo(){
const y = 0
class B{
bar(){
this.x // Property 'x' does not exist on type 'B'
console.log(y)
}
}
return B
}
}

```

因为有B包的逻辑,就不太适合再增加,让嵌套类可以访问外部类成员的逻辑了。 逻辑太复杂,不利于程序员写程序。



## 类型守卫

```tsx
class FileSystemObject {
isFile(): this is FileRep {
return this instanceof FileRep;
}
isDirectory(): this is Directory {
return this instanceof Directory;
}
isNetworked(): this is Networked & this {
return this.networked;
}
constructor(public path: string, private networked: boolean) {}
}

class FileRep extends FileSystemObject {
constructor(path: string, public content: string) {
super(path, false);
}
}

class Directory extends FileSystemObject {
children: FileSystemObject[];
}

interface Networked {
host: string;
}

const fso: FileSystemObject = new FileRep("foo/bar.txt", "foo");

if (fso.isFile()) {
fso.content;
//const fso: FileRep
} else if (fso.isDirectory()) {
fso.children;
//const fso: Directory
} else if (fso.isNetworked()) {
fso.host;

//const fso: Networked & FileSystemObject
}
```



## 抽象类

抽象类不可以实例化

```tsx
abstract class Base {
abstract getName(): string;

printName() {
console.log("Hello, " + this.getName());
}
}

const b = new Base();
// Cannot create an instance of an abstract class.
```

必须继承:

```tsx
class Derived extends Base {
getName() {
return "world";
}
}

const d = new Derived();
d.printName();
```



## 类型的关系



TS会通过成员去思考类型是否一致:

```tsx
class Point1 {
x = 0;
y = 0;
}

class Point2 {
x = 0;
y = 0;
}

// OK
const p: Point1 = new Point2();
```

另一个例子:

```tsx
class Person {
name: string;
age: number;
}

class Employee {
name: string;
age: number;
salary: number;
}

// OK
const p: Person = new Employee();
```

最后一个例子:

```tsx
class Empty {}

function fn(x: Empty) {
// can't do anything with 'x', so I won't
}

// All OK!
fn(window);
fn({});
fn(fn);
```