Typescript面向对象---下篇

简介: Typescript面向对象---下篇

接口


接口的作用类似于抽象类,不同点在于接口中的所有方法和属性都是没有实值的,换句话说接口中的所有方法都是抽象方法。接口主要负责定义一个类的结构,接口可以去限制一个对象的接口,对象只有包含接口中定义的所有属性和方法时才能匹配接口。同时,可以让一个类去实现接口,实现接口时类中要保护接口中的所有属性。


TypeScript 中,我们使用接口(Interfaces)来定义对象的类型

接口: 是对象的状态(属性)和行为(方法)的抽象(描述)

接口类型的对象 :


1. 多了或者少了属性是不允许的

2. 可选属性: ?

3. 只读属性: readonly


现在我们做一个需求帮助大家更好的理解接口:创建人的对象, 需要对人的属性进行一定的约束


id是number类型, 必须有, 只读的

name是string类型, 必须有

age是number类型, 必须有

sex是string类型, 可以没有



// 定义人的接口
interface IPerson {
  id: number
  name: string
  age: number
  sex: string
}
const person1: IPerson = {
  id: 1,
  name: 'james',
  age: 37,
  sex: '男'
}


注意: 类型检查器会查看对象内部的属性是否与 IPerson 接口描述一致, 如果不一致就会提示类型错误


可选属性


接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。



interface IPerson {
  id: number
  name: string
  age: number
  sex?: string
}


带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个 ? 符号


可选属性的好处之一是可以对可能存在的属性进行预定义,好处之二是可以捕获引用了不存在的属性时的错误



const person2: IPerson = {
  id: 1,
  name: 'james',
  age: 37
  // sex: '男' // 可以没有
}


只读属性


一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用 readonly 来指定只读属性:



interface IPerson {
  readonly id: number
  name: string
  age: number
  sex?: string
}


一旦赋值后再也不能被改变了。



const person2: IPerson = {
  id: 2,
  name: 'harden',
  age: 33
  // sex: '男' // 可以没有
  // score: 12 // error 没有在接口中定义, 不能有
}
person2.id = 13 // error


  • readonly vs const


最简单判断该用 readonly 还是 const 的方法是看要把它做为变量使用还是做为一个属性。 做为变量使用的话用 const,若做为属性则使用 readonly。


函数类型


接口能够描述 JavaScript 中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。


为了使用接口表示函数类型,我们需要给接口定义一个调用签名。它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。



/*
接口可以描述函数类型(参数的类型与返回的类型)
*/
interface SearchFunc {
  (source: string, subString: string): boolean
}


这样定义后,我们可以像使用其它接口一样使用这个函数类型的接口。 下例展示了如何创建一个函数类型的变量,并将一个同类型的函数赋值给这个变量。



const mySearch: SearchFunc = function(source: string, sub: string): boolean {
  return source.search(sub) > -1
}
console.log(mySearch('abcd', 'bc'))


类类型


定义类时,可以使类实现一个接口,实现接口就是使类满足接口的要求,实现接口需要使用一个关键字implements


类实现接口



interface Alarm {
  alert(): any
}
interface Light {
  lightOn(): void
  lightOff(): void
}
class Car implements Alarm {
  alert() {
    console.log('Car alert')
  }
}


一个类可以实现多个接口



class Car2 implements Alarm, Light {
  alert() {
    console.log('Car alert')
  }
  lightOn() {
    console.log('Car light on')
  }
  lightOff() {
    console.log('Car light off')
  }
}


接口继承接口


和类一样,接口也可以相互继承。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里。


interface LightableAlarm extends Alarm, Light {}


泛型


定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定),此时泛型便能够发挥作用。


举例



function test(arg: any): any{
    return arg;
}


上例中,test函数有一个参数类型不确定,但是能确定的是其返回值的类型和参数的类型是相同的,由于类型不确定所以参数和返回值均使用了any,但是很明显这样做是不合适的,首先使用any会关闭TS的类型检查,其次这样设置也不能体现出参数和返回值是相同的类型,这样的话跟js还有什么区别呢?

使用泛型:



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


这里的  就是泛型,T是我们给这个类型起的名字(不一定非叫T),设置泛型后即可在函数中使用T来表示该类型。所以泛型其实很好理解,就表示某个类型。

那么如何使用上边的函数呢?


  • 方式一(直接使用):

test(10)

使用时可以直接传递参数使用,类型会由TS自动推断出来,但有时编译器无法自动推断时还需要使用下面的方式


  • 方式二(指定类型):

test(10)

也可以在函数后手动指定泛型

多个泛型参数的函数

一个函数可以定义多个泛型参数,泛型间使用逗号隔开:



function test<T, K>(a: T, b: K): K{
    return b;
}
test<number, string>(10, "hello");


使用泛型时,完全可以将泛型当成是一个普通的类去使用


泛型接口


在定义接口时, 为接口中的属性或方法定义泛型类型在使用接口时, 再指定具体的泛型类型



interface IbaseCRUD<T> {
  data: T[]
  add: (t: T) => void
  getById: (id: number) => T
}
class User {
  id?: number //id主键自增
  name: string //姓名
  age: number //年龄
  constructor(name, age) {
    this.name = name
    this.age = age
  }
}
class UserCRUD implements IbaseCRUD<User> {
  data: User[] = []
  add(user: User): void {
    user = { ...user, id: Date.now() }
    this.data.push(user)
    console.log('保存user', user.id)
  }
  getById(id: number): User {
    return this.data.find(item => item.id === id)
  }
}
const userCRUD = new UserCRUD()
userCRUD.add(new User('tom', 12))
userCRUD.add(new User('tom2', 13))
console.log(userCRUD.data)


泛型类


在定义类时, 为类中的属性或方法定义泛型类型 在创建类的实例时, 再指定特定的泛型类型



class GenericNumber<T> {
  zeroValue: T
  add: (x: T, y: T) => T
}
let myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = function(x, y) {
  return x + y
}
let myGenericString = new GenericNumber<string>()
myGenericString.zeroValue = 'abc'
myGenericString.add = function(x, y) {
  return x + y
}
console.log(myGenericString.add(myGenericString.zeroValue, 'test'))
console.log(myGenericNumber.add(myGenericNumber.zeroValue, 12))


泛型约束


如果我们直接对一个泛型参数取 length 属性, 会报错, 因为这个泛型根本就不知道它有这个属性



// 没有泛型约束
function fn<T>(x: T): void {
  // console.log(x.length)  // error
}


我们可以使用泛型约束来实现



interface Lengthwise {
  length: number
}
// 指定泛型约束
function fn2<T extends Lengthwise>(x: T): void {
  console.log(x.length)
}


我们需要传入符合约束类型的值,必须包含必须 length 属性:



fn2('abc')
// fn2(123) // error  number没有length属性


使用T extends Lengthwise表示泛型T必须是Lengthwise的子类.


小结


到这里,面向对象的知识就到尾声了,同时typescript的知识暂停一段落,希望各位能够在这个系列中学到一些东西,主要是需要会应用,一切在实践过后才能够充分掌握。


相关文章
|
JavaScript 关系型数据库 编译器
TypeScript与面向对象
TypeScript与面向对象
|
JavaScript 前端开发 Java
typescript(ts)面向对象之装饰器
每一个属性都要验证,我们就要写一个if 来进行判断,100个属性就要写100遍,然后每一个if中的条件还有可能相同,就会导致我们写重复的代码,这是非常麻烦的。
typescript(ts)面向对象之装饰器
|
JavaScript 索引
ts(typescript)面向对象之索引器
在TS中,默认情况下,不对索引器(成员表达式)做严格的类型检查使用配置noImplicitAny:true开启对隐式any的检查。
ts(typescript)面向对象之索引器
|
JavaScript
ts(typescript)面向对象之静态成员
在js中,构造函数也是函数,只是特殊的地方在于,构造函数相对于普通函数来说。使用new 的方式来创建,并且构造函数里面是有this的。这个this 的指向是当前构造函数的对象。而普通的函数的this的指向是指向全局window的。
ts(typescript)面向对象之静态成员
|
设计模式 JavaScript
ts(typescript)面向对象之类的抽象类
模板模式:有些方法,所有的子类实现的流程完全一致,只是流程中的某个步骤的具体实现不一致,可以将该方法提取到父类,在父类中完成整个流程的实现(上面代码中的attack()),遇到实现不一致的方法时(把该方法做成抽象方法,字类必须去实现),将该方法做成抽象方法。这种设计模式是大名鼎鼎的 模板模式
ts(typescript)面向对象之类的抽象类
|
JavaScript
ts(typescript)面向对象之类的继承
super当作属性使用的时候, super 指向的是父类的原型,因此super无法拿到父类的实例属性,只能拿到父类的public和protected的方法,super 如果当方法使用的话,只能在字类的构造函数中并且是第一行使用,需要使用super()来实例化父类里面的属性,确保字类可以获取父类的成员。
ts(typescript)面向对象之类的继承
|
JavaScript
【TypeScript教程】# 7:面向对象简介
【TypeScript教程】# 7:面向对象简介
70 0
|
JavaScript 前端开发 Java
Typescript面向对象---上篇
Typescript面向对象---上篇
Typescript面向对象---上篇
|
JavaScript 前端开发 Java
TypeScript 面向对象程序设计(OOP)
在 JavaScript 中 ES6 之前我们使用函数(构造器函数)和基于原型来创建一个自定义的类,但这种方式总会让人产生困惑,特别是习惯了 Java、PHP 等面向对象编程的同学来说更加难以理解。 抽象一个类 面向对象编程的基本单位是对象,但对象又是由类实例化的,所以我们第一步需要先知道怎么去声明一个类。 类的声明 类的声明使用 class 关键词,类名与变量、函数等命名规则类似,这里要首写字母大写,类名后跟上一对花括号可以理解为的类的主体,类的主体三部分组成:成员属性、构造函数、成员方法。
116 0
|
1月前
|
JavaScript 前端开发 安全
深入理解TypeScript:增强JavaScript的类型安全性
【10月更文挑战第8天】深入理解TypeScript:增强JavaScript的类型安全性
46 0