duck typing or structural subtyping
interface LabeledValue {
label: string;
}
function printLabel(labeledObj: LabeledValue) {
console.log(labeledObj.label);
}
let myObj = { size: 10, label: "Size 10 Object" };
// 这里必须是以变量的形式,如果是 printLabel({ size: 10, label: "Size 10 Object" })
// 相当于 const labeledObj: LabeledValue = { size: 10, label: "Size 10 Object" }。会报错
printLabel(myObj);
在给函数以变量的形式传递值时,只要传递的实参包含形参声明的全部属性即可。
Optional Properties
interface people {
name?: string
}
type people = {
name?: string
}
function a(p?: people) {}
Readonly properties
interface people {
readyonly name: string
}
type people = {
readonly name: string
}
// 如果直接用在变量声明时,只能用在 array 或则 元祖类型上
const ro: readonly [] = []
// 如果要用泛型来声明数组,要加 readonly 的话,可以使用 ts 提供的 ReadonlyArray
let ro: ReadonlyArray<number>
readyonly 是属性修饰符,所以是用来修饰属性的,数组内的属性不能变,包含数组的营引用不能改变。而 const 是指这个数组的引用不能改变。
Function Types
interface 能够描述 JavaScript 对象的任何形式,包括函数。除了通过属性来描述对象,接口还可以描述函数类型。
// 注意这整个 func 就是一个函数类型,增加的其他属性,例如 height 会被当做 func.height 来进行类型约束
interface func {
(name: string, age: number): string
height: number
}
type func = {
(name: string, age: number): string
}
type func = (name: string, age:number) => string
const test: func = (name: string, age: number) => { // 这里函数参数的类型和返回值的类型可以不指定,直接用 func 的。
return `${name} ${age}`
}
Indexable Types
还可以使用接口来描述可以索引到的类型,indexable types 有一个 index signature 用来当做访问该对象的一个索引。例如 a[10]、a['name']
中的 10 和 ‘name’。表示我们使用某种 index signature 索引对象中对应的值时,返回什么类型的值。
// 索引器为 number 类型,访问时返回一个 string 类型的值
interface StringArray {
// index 可以随便取名,不影响
[index: number]: string;
// 声明 indexable types 后,在该对象内部添加类型时,也需要遵循该原则。因为 xxx.name 跟 xxx['name']是一样的,也是通过 'name' 索引去找值
name: number
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
index signature 只能为 number 类型或者 string 类型。如果在一个对象中,同事使用 number 和 string 作为 index signature,number 类型返回的值的类型必须是 string 类型返回值的子类型,因为 number 作为索引器时,最终会被转换为 string 类型的,如下:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
interface NotOkay {
[x: number]: Dog;
[x: string]: Animal;
}
Class Types
interface 可以被 class 类 implements。注意这里声明了一个具体的方法 teat,跟前面说到的使用 interface 声明函数的类型不一样。这里相当于声明了一个 interface 包含了各种属性,需要 class 去实现。就像使用 type 声明了一个对象,里面包含各种属性的类型。
interface NumberDictionary {
name: string
teat(name: string, age: number): string
}
class test implements NumberDictionary {
name: string
teat(name: string, age: number): string {}
}
注意 interface 用来被 implement 时,使用来描述 class 的实例&&public 属性。
给类声明类型
如果要给类的实例声明类型,类型只要包含实例属性即可。
interface test {
tick: () => void
}
class DigitalClock {
tick() {
console.log("beep beep");
}
}
const constrc: test = new DigitalClock()
如果要给类本身声明类型,可以像下面这样。注意给类本身声明类型,其实就是给构造器进行类型声明,不能添加其他属性
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface
}
Extending Interfaces
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = {} as Square;
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
Interfaces Extending Classes
interface 也可以继承 class 的类型,但是不包括它们的实现,只包括成员的类型,其中还包括 private、protect 类型的成员。如果继承的类中包含 private、protect 成员,只有该类的子类才能 implements 该 interface,因为只有这样 private 成员才都是是同样都起源于同一个 class。
class People {
private name1: string
private age: number
constructor(name1: string, age: number) {
this.age = age
this.name1 = name1
}
}
interface student extends People {
school: string,
score: number
}
class Student extends People implements student {
school: string
score: number
constructor(age: string, name1: number, score: number, school: string) {
super(age, name1)
this.score = score
this.school = school
}
}