接口
可选属性
接口里的属性不全是必需的。可以先定义在接口里,但是属性名字定义的时候需在后面加一个?
符号。
例如一些值类型的属性,在 ts 里面是不能为空的,如果为空则会报错。但是在后面加一个?符号,则不会报错,说明这个属性可以为空,为可选属性,可有可无。
interface abc {
color?: string
width?: number
}
function creatSquare(config: abc): { color: string; area: number } {
let newabc = { color: "white", area: 100 }
if (config.color) {
newabc.color = config.color
}
if (config.width) {
newabc.area = config.width * config.width
}
return newabc
}
let myabc = creatSquare({ color: "black" })
复制代码
只读属性
一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用readonly
来指定只读属性:
interface Point {
readonly x: number
readonly y: number
}
复制代码
你可以通过赋值一个对象字面量来构造一二个 Point。赋值后,x 和 y 再也不能被改变了。
let p1: Point = { x: 10, y: 20 }
p1.x = 5 // error
复制代码
TS 有ReadonlyArray<T>
类型,它与 Array相似,只是把所有可变方法去掉了,确保数组创建后再也不能被修改。
let a: number[] = [1, 2, 3, 4] //创建普通数组
let ro: ReadonlyArray<number> = a //创建一个不可变的数组,等于普通数组a
ro[0] = 12 //error
ro.push(5) //error
ro.length = 100 //error
a = ro //error
复制代码
上面代码的最后一行,可以看到就算把整个 ReadonlyArray 赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写:
a = ro as number[]
复制代码
readonly vs const
-
什么时候用哪一个?
- 作为变量:const
- 作为属性:readonly
函数类型
为了使用接口表示函数类型,我们需要给接口定义一个调用签名。 它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
interface SearchFunc {
//括号里类似定义参数名字和类型
//冒号后定义的函数返回值的类型
(source: string, subString: string): boolean
}
复制代码
如何调用?
let mySearch: SearchFunc
mySearch = function(source: string, subString: string) {
let res = source.search(subString)
return res > -1
}
复制代码
函数类型的类型检查,函数的参数名不需要与接口里定义的名字相比配,相当于形参实参的意思相似。
let mySearch: SearchFunc
mySearch = function(src: string, sub: string) {
let res = src.search(sub)
return res > -1
}
复制代码
可索引类型
interface StringArray {
[index: number]: string //这是一个索引签名,意思是用number类型去索引StringArray,返回值为string类型
}
复制代码
- 共支持两种索引签名
- 字符串
- 数字
- 重点:数字索引的返回值必须是字符串索引返回值类型的子类型
类类型
Implements 与 Extends 的区别
extends
,表示对父类的继承,可以实现父类,也可以调用父类初始化this.parent()
.而且会覆盖父类定义的变量或者函数。implements
,表示对接口的实现,接口通过关键字interface
进行定义。eg:public class S implements F
,在接口F
中对方法进行声明,在类S
中对该方法进行实现。
实现接口
TypeScript 也能够用它来明确的强制一个类去符合某种契约。
interface ClockInterface {
currentTime: Date
}
class Clock implements ClockInterface {
currentTime: Date
constructor(h: number, m: number) {}
}
复制代码
也可以在接口中定义一个方法,在类里实现它,如同下面的 setTime 方法一样:
interface ClockInterface {
currentTime: Date
setTimeout(d: Date)
}
class Clock implements ClockInterface {
currentTime: Date
setTimeout(d: Date) {
this.currentTime = d
}
constructor(h: number, m: number) {}
}
复制代码
接口描述了类的公共部分,而不是公共和私有两部分。
类静态部分与实例部分的区别
(这一部分不是很懂)
继承接口
和类一样,接口也可以相互继承
interface Shape {
color: string
}
interface PenStroke {
penWidth: number
}
interface Square extends Shape, PenStroke {
slideLength: number
}
let square = <Square>{}
square.color = "yellow"
square.penWidth = 10
square.slideLength = 5.0
复制代码
混合类型
示例:一个对象可以同时作为函数和对象使用,并带有额外的属性
interface Counter {
(start: number): string //作为函数来定义,参数为数字类型,返回值为字符串类型
interval: number //作为对象定义
reset(): void //定义一个方法
}
function getCounter(): Counter {
let counter = <Counter>function(start: number) {}
counter.interval = 123
counter.reset = function() {}
return counter
}
let c = getCounter()
c(10)
c.reset()
c.interval = 5.0
复制代码
接口继承类
公共,私有与受保护的修饰符 public/private/protected 的具体区别
- public:是指这个函数可以被其他的类来调用,也可以被自己类里的函数来调用。 在 TypeScript 里,成员都默认为 public。
- protected:是指这个函数可以被继承类调用,也可以被自己类里的函数来调用。当成员被标记 private 时,它就不能再声明它的类的外部访问。
class Animal { private name: string constructor(theName: string) { this.name = theName } } new Animal("Cat").name // 错误: 'name' 是私有的. 复制代码
- private:只能被自己类里的其他函数调用,其他的一概不能调用
当接口继承一个类类型时,它会继承类的成员,但是!不包括其实现。(就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样)
接口继承到类的 private 和 protected 成员,那你这个接口类型就只能被这个类或其子类所实现,权限问题。
//类类型
class Control {
private state: any //私有成员
}
//定义一个 a 接口来继承Control这个类,因为 a 接口集成后,则它包含了Control这个弗雷德所有成员,包括私有成员 state
// state作为私有成员,所以只能是Control的子类们才能实现 a 接口
//只有Control子类才能够拥有父类的私有成员 state
interface a extends Control {
do(): void
}
//C作为子类继承Control父类,并且实现 a 接口方法
class C extends Control implements a {
do() {}
}
// D作为子类继承Control父类
class D extends Control {
do() {}
}
//下面这个就会报错了,因为 Image 这个类并不是 Control 的子类,所以不能用 a 接口
class Image implements a {
do() {}
}
复制代码