Skip to content

Commit 22d8a21

Browse files
committed
更新TypeScript中的inerface接口、类的定义与继承、类中的访问类型和构造器小节
1 parent ecbd2c4 commit 22d8a21

File tree

1 file changed

+273
-5
lines changed

1 file changed

+273
-5
lines changed

docs/TypeScript/README.md

Lines changed: 273 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ const newData = JSON.parse(rowData)
103103
但是这种情况我们使用编辑器将鼠标放置在`newData`上我们会发现`ts`无法推断出`newData`的类型,结果为`any`,像这种使用内置函数方法的情况,我们可以这样处理:
104104
```ts
105105
interface Person = {
106-
name: 'string',
106+
name: 'string';
107107
}
108108
const rowData = '{"name": "haochyk"}'
109109
const newData: Person = JSON.parse(rowData)
@@ -214,8 +214,8 @@ const objectArr: { name: string, age: number }[] = [
214214
如果我们的对象内容非常多的话,这样写代码就显的非常不美观了,我们可以使用类型别名(`type alias`)来解决这个问题:
215215
``` ts
216216
type User = {
217-
name: string,
218-
age: number,
217+
name: string;
218+
age: number;
219219
}
220220
const objectArr: User[] = [
221221
{
@@ -227,8 +227,8 @@ const objectArr: User[] = [
227227
值得我们注意的是`class`类,`TS`不会强制要求必须返回实例对象,所以只要数据内容格式一致都是被允许的,比如以下这种写法:
228228
``` ts
229229
class User = {
230-
name: string,
231-
age: number,
230+
name: string;
231+
age: number;
232232
}
233233
const objectArr: User[] = [
234234
new User(),
@@ -261,4 +261,272 @@ const userInfo: ( string | number )[] = ['haochy', 'male', 18]
261261
const userInfoList: [string, string, number][] = [
262262
['haochyk', 'male', 18],
263263
]
264+
```
265+
## Interface接口
266+
首先我看来看一段代码,我们通过代码来讲解interface的具体知识点。
267+
``` ts
268+
function getPersonName (person: { name: string }) {
269+
console.log(person.name)
270+
}
271+
function setPersonName (person: { name: string }, name: string) {
272+
person.name = name
273+
}
274+
```
275+
上面两个简单的方法我们可以看出person参数后的类型定义我们需要重复的写两边,这个时候我们将重复的类型定义用interface接口的形式抽离出来:
276+
``` ts
277+
interface Person {
278+
name: string;
279+
age: number;
280+
}
281+
function getPersonName (person: Person): void {
282+
console.log(person.name)
283+
}
284+
function setPersonName (person: Person, name: string): void {
285+
person.name = name
286+
}
287+
```
288+
当然还有另外一种方法可以实现相通的效果就是使用类型定义:`type alias`:
289+
``` ts
290+
type Person = {
291+
name: string;
292+
age: number;
293+
}
294+
function getPersonName (person: Person): void {
295+
console.log(person.name)
296+
}
297+
function setPersonName (person: Person, name: string): void {
298+
person.name = name
299+
}
300+
```
301+
`interface``type`类似但又不完全相同,不同点就是interface只能代表一个函数或者一个对象,它不能代表一个基础类型:
302+
``` ts
303+
type Person = string
304+
interface Person {
305+
name: string;
306+
age: number;
307+
}
308+
```
309+
::: tip 提示
310+
`TypeScript`里面一个通用型的规范就是:如果能用接口来表述一个别名的话我们就用接口的方式,实在不行我们才用类型别名
311+
:::
312+
在有些情况下我们不需要传递`age`属性该怎么办,我们不传递`age`参数`ts`又会报错,我们可以这样来写:
313+
``` ts
314+
interface Person {
315+
readyonly name: string;
316+
age?: number;
317+
}
318+
```
319+
这样的意思就是`age`属性可有可无,还有一个修饰符:`readonly`意思为属性只读。
320+
这里值得我们注意的一点就是,如果我们传递参数的时候,多传递了一个`sex`属性:
321+
``` ts
322+
interface Person {
323+
name: string;
324+
age: number;
325+
}
326+
function getPersonName (person: Person): void {
327+
console.log(person.name)
328+
}
329+
function setPersonName (person: Person, name: string): void {
330+
person.name = name
331+
}
332+
const person = {
333+
name: 'haochyk',
334+
sex: 'male',
335+
}
336+
// 不会报错
337+
getPersonName(person)
338+
// 报错
339+
getPersonName({
340+
name: 'haochyk',
341+
sex: 'male',
342+
})
343+
```
344+
这是因为,我们如果直接使用字面量的形式传参的话,`ts`会进行强校验,必须严格符合参数的类型定义,而如果我们使用缓存的形式,则不会,只要有类型定义该有的东西即可,多出一点东西也是可以的。
345+
如果我们只是确定参数对象有`name`属性,我们不确定有其他属性的时候我们可以这样来写:
346+
``` ts
347+
interface Person {
348+
readyonly name: string;
349+
age?: number;
350+
[propName: string]: any;
351+
}
352+
```
353+
接口里不仅可以存这样的属性和它的类型还可以存方法,比如我们定义`say`方法返回值的类型为`string`
354+
``` ts
355+
interface Person {
356+
readyonly name: string;
357+
age?: number;
358+
[propName: string]: any;
359+
say(): string;
360+
}
361+
```
362+
`Class`类是可以应用接口的,当一个类去应用接口时必须拥有接口里的属性,举个例子:
363+
``` ts
364+
class user implements Person {
365+
name = 'haochy';
366+
say () {
367+
return 'hello'
368+
}
369+
}
370+
```
371+
接口之间还可以互相继承,如下面这个例子:
372+
``` ts
373+
interface Teacher extends Person {
374+
teach(): string
375+
}
376+
const teacher = {
377+
name: 'haochyk',
378+
age: 18,
379+
say () {
380+
return 'hello'
381+
},
382+
teach () {
383+
return 'TypeScript'
384+
}s
385+
}
386+
setPersonName(teacher, 'haochyk')
387+
```
388+
接口继承它会拥有`Person`接口下所有的属性和方法,同时还必须得有自己的属性或方法。
389+
接口自身除了可以定义属性、方法之外,其实它自身还可以定义函数:
390+
``` ts
391+
interface SayHi {
392+
(word: string): string
393+
}
394+
const say: SayHi = (word) => {
395+
return word
396+
}
397+
```
398+
同样`interface`还可以定义数组这样的索引类型,当我们去写这种接口的时候,`ts`最终会把把文件编译成js,但是最终编译后的`js`内并没有`interface`
399+
::: danger 注意
400+
其实,`interface`就是在我们开发过程中`TypeScript`帮助我们做语法提示的一个工具。真正编译的时候会将这部分内容剔除掉。
401+
:::
402+
## 类的定义与继承
403+
`TypeScript`中的类其实和`JavaScript``ES6`中的类很类似,不过在它的基础上`TypeScript`提供了更多的特性.
404+
我们先看一个最基础的类:
405+
``` ts
406+
class Person {
407+
name = 'haochyk';
408+
getName () {
409+
console.log(this.name)
410+
}
411+
}
412+
```
413+
有了类之后我们可以通过类来创建一个实例,比如说:
414+
``` ts
415+
class Person {
416+
name = 'haochyk';
417+
getName () {
418+
return this.name
419+
}
420+
}
421+
const person = new Person()
422+
```
423+
到这里我们就说了如何去定义一个类,以及如何在类里去定义方法。
424+
接着我们来说下类的继承:(在`ES6`里写类的继承其实是和`TypeScript`里是一样的)
425+
``` ts
426+
class Person {
427+
name = 'haochyk';
428+
getName () {
429+
return this.name
430+
}
431+
}
432+
class Teacher extends Person {
433+
getTeacherName () {
434+
return 'hao'
435+
}
436+
}
437+
const teacher = new Teacher()
438+
console.log(teacher.getName()) // haochyk
439+
console.log(teacher.getTeacherName()) // hao
440+
```
441+
继承的意思就是,子类不仅可以使用父类的方法还可以使用自己的方法。
442+
类还有一个概念叫做重写,即在子类和父类中的同名方法,子类中的方法会覆盖掉父类中的方法,如果想要调用父类中的方法,我们可以使用`super`,例如:
443+
``` ts
444+
class Teacher extends Person {
445+
getName () {
446+
return super.getName() + '1'
447+
}
448+
}
449+
console.log(teacher.getName()) // haochyk1
450+
```
451+
这同样也是`super`在开发中常用的应用场景:子类重写父类方法,如果需要调用父类方法可以使用`super`
452+
## 类中的访问类型和构造器
453+
### 访问类型
454+
什么是访问类型?我们在`ts`中定义一个类,我们实例化这个类,访问以及修改这个实例中的属性都是可以的,因为`ts`中类的属性默认是`public`访问类型。
455+
访问类型分为三种:`private``protected``public`。<br />
456+
* `private`: 仅在类内允许被调用
457+
* `protected`:类内或者继承的子类中允许被调用
458+
* `public`:类内外都可以允许被调用
459+
我们通过代码来看下这三个的区别:<br />
460+
首先`private`
461+
``` ts
462+
class Person {
463+
private name = 'haochyk'
464+
say () {
465+
return this.name // 允许访问
466+
}
467+
}
468+
const person = new Person()
469+
console.log(person.name) // ts报错
470+
```
471+
`public`
472+
``` ts
473+
class Person {
474+
public name = 'haochyk'
475+
say () {
476+
return this.name // 允许访问
477+
}
478+
}
479+
const person = new Person()
480+
console.log(person.name) // 允许访问
481+
```
482+
`protected`
483+
``` ts
484+
class Person {
485+
protected name = 'haochyk'
486+
say () {
487+
return this.name // 允许访问
488+
}
489+
}
490+
class Teacher {
491+
teacherSay () {
492+
return this.name // 允许访问
493+
}
494+
}
495+
const person = new Person()
496+
console.log(person.name) // ts报错
497+
```
498+
### 构造器 (constructor)
499+
老样子,我们先来定义一个类:
500+
``` ts
501+
class Person {
502+
public name: string
503+
constructor (name: string) {
504+
this.name = name
505+
}
506+
}
507+
const person = new Person('haochyk')
508+
console.log(person.name) // haochyk
509+
```
510+
`constructor`这个方法会在类被实例化的时候自动执行,并且将实例化的参数传递给`constructor`这个方法。
511+
以上例子是比较传统的写法,我们先定义一个属性,然后在构造器中给属性赋值,`ts`提供了一个更简单的方法,这两种写法是等价的:
512+
``` ts
513+
class Person {
514+
constructor (public name: string) {}
515+
}
516+
const person = new Person('haochyk')
517+
console.log(person.name) // haochyk
518+
```
519+
如果继承中,子类要使用构造器,需要使用`super`,这时`super`是一个方法,它代表父类的构造函数,同时需要将父类构造函数需要的参数传递给`super`方法,即便父类没有构造器,子类也需要调用一个参数为空的`super()`,代码如下:
520+
``` ts
521+
class Person {
522+
constructor (public name: string) {}
523+
}
524+
class Teacher {
525+
constructor (public age: number) {
526+
super('haochyk')
527+
}
528+
}
529+
const teacher = new Teacher(18)
530+
console.log(teacher.name) // haochyk
531+
console.log(teacher.age) // 18
264532
```

0 commit comments

Comments
 (0)