typescript学习笔记——类和函数

2019/06/08

ES6之后的javascript也拥有了类这个概念,typescript也针对es6的类制定了一些规则。

定义类

值得强调的是,这里初始化name和position的时候并没有一个定义值,所以都是undefined,但也不会报错。因为null和undefined是所有基本类型的子类,但并不推荐这么做,准确的写法应该是

class Animal {
name: string = ''
position: number = 0

constructor(name: string, position: number) {
this.name = name
this.position = position
}

move(distance: number) {
this.position += distance
}
}

const xiaohuang = new Animal('xiaohuang', 1)

类的继成

继成的时候必须要super父类的初始化参数,如果不写也会默认去super所有的参数,但需要意识到这个super的重要性,super意味着子类可以拥有父类的constructor的初始化的功能

class Dog extends Animal {
constructor(name, position) {
super(name, position)
}

bark() {
console.log('I am a cute dog')
}
}

// 默认super所有参数
class Dog extends Animal {
bark() {
console.log('I am a cute dog')
}
}

public, private和protected

公有的属性和方法是可以在任何地方随意访问的。typescript默认所有的属性和方法都是共有的,所有使用public关键字是多余的,而在很多其他语言里,必须显式声明公有的属性和方法。

私有属性和方法只能在类的内部访问,其他地方使用就会编译报错,注意,只是编译报错,typescript并不会产生真正的私有属性和变量,因为es6并没有提供这个功能。所以编译依然可以通过,编译出来的文件依然可以访问私有属性和方法。

这也说明typescript并不能改变js的任何能力,他能做的只是帮你校验代码的正确性和安全性。

class Animal {
public type: string = 'animal'
private age: number = 18
protected tag: number = 1
}

class Dog extends Animal {
sayAge() {
console.log(this.age)
console.log(this.tag)
}
}

protected属性和private属性类似,不可以在外部访问这个值。区别在于protected的属性可以在子类中访问和修改而private属性只能在定义他的类中访问和修改。被保护的含义就是只能继承之后用,不能直接用,所以如果希望自己定义的类只能被继成后使用,不能直接实例的话就需要把constructor设置为protected。这样他就只能被继承之后再使用了。

其他的一些功能

静态属性 使用static定义的属性会作为类的静态属性

readonly 上一篇又说到,readonly在类中的表现为设置为readonly的属性在constructor实例化结束后就无法被修改了

存取器 get和set可以定义类的属性获取和设置的表现,在ts中如果只设置get不设置set那么这个值就会变成只读

把类当作接口使用

下边的两种代码是等价的,类在ts中可以被当作接口来用,因为声明一个类的同时也会声明完备这个类的类型,相当于声明了一个接口。

interface Point {
x: number
y: number
}

interface Point3D extends Point {
z: number
}

let dot: Point3D = { x: 1, y: 2, z: 3 }

class Point {
x: number
y: number
}

interface Point3D extends Point {
z: number
}

let dot: Point3D = { x: 1, y: 2, z: 3 }

函数

参数和返回值

函数会根据参数自动推断出返回值是是什么,所以很多情况下并不需要定义返回值的类型,只需要定义参数的类型就好了,当然显式的说明返回值对于其他开发人员来说是非常友好的,这是一个很好的习惯。

函数的参数和返回值

function add(x: number, y: number) {
return x + y
}

函数的返回值

function add(x: number, y: number): number {
return x + y
}

定义函数类型

在上一篇的接口中里略带的说明了函数的定义方法

下边的两种方式声明出来的效果是一样的,这里用到了type这种声明类型的方式,在后边会说,但这里可以看出函数既是一种接口又是一种类型,就像js中的函数是一个对象一样。

定义接口或者类型,函数在声明的时候可以根据接口自动识别参数的类型,所以下边的这两种方式声明的函数不需要再声明x和y以及返回值是什么了

interface Add {
(x: number, y: number): number
}

const add: Add = (x, y) => {
return x + y
}

type Add = (x: number, y: number) => number

const add: Add = (x, y) => {
return x + y
}

可选参数,默认参数和剩余参数

可选参数和接口的可选类型一样,而默认参数是es6提供的一个新功能,并且typescript会根据设置的默认参数的类型隐式的设置这个参数的类型。

剩余参数一般情况下是个数组,所以需要用数组的方式表示剩余参数。

type Add = (...numbers: number[]) => number

const add: Add = (...nubmers) => {
return nubmers.reduce((res, i) => res + i, 0)
}

重载

函数最复杂的点在于他的不确定性,比如根据不同的参数返回不同类型的结果。

比如pickCard函数如果传入一个card会返回这个card的index,传入一个index会返回一个card。

需要使用下边这种写法

interface Card {
text: string
type: string
}

function pickCard(x: number): Card | null
function pickCard(x: Card): number
function pickCard(x): Card | number | null {
const cards: Card[] = [
{ text: 'A', type: 'alpha' },
{ text: 'B', type: 'alpha' },
{ text: 'C', type: 'alpha' }
]

if (typeof x === 'number') {
return cards[x]
}

if (typeof x === 'object') {
const foundCard = cards.findIndex(item => item.text === x.text)
}
}

pickCard({ text: 'A', type: 'alpha' }) // 0
pickCard(0) // { text: 'A', type: 'alpha' }

重载的写法非常的别扭,但也是为了可以清晰的看出函数重载的过程,typescript根据函数定义列表去处理函数的调用。

嗨,请先登录

加载中...
(๑>ω<๑) 又是元气满满的一天哟