共计 2985 个字符,预计需要花费 8 分钟才能阅读完成。
接口
在 JavaScript 中没有这个概念。
接口初探
function printLabel(labelledObj: { label: string}) {console.log(labelledObj.label);
}
// label 为传入对象参数必有的一个属性
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
下面利用接口重写例子
// 个人理解为将传入参数要求提出来,看起来更加清晰
interface LabelledValue {label: string;}
function printLabel(labelledObj: LabelledValue) {console.log(labelledObj.label)
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。
可选属性
接口里的属性不全都是必需的。
interface SquareConfig {
color?:string; // 注意这里是分号哟
width?:number;
}
function createSquare(config:SquareConfig):{color:string;area:number}{let newSquare = {color: "white", area: 100};
if (config.color) {newSquare.color = config.color;}
if (config.width) {newSquare.area = config.width * config.width;}
return newSquare;
}
let mySquare = createSquare({color: "black"});
只读属性
只读属性创建后不能再次修改。
interface Point {
readonly x: number;
readonly y: number;
}
let p1: Point = {x: 10, y: 20};
p1.x = 5; // error!
下面的代码可以让 ro 数组创建后再也无法改变
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
a = ro as number[]; //right
readonly
VS const
官方解说:最简单判断该用 readonly
还是 const
的方法是看要把它做为变量使用还是做为一个属性。做为变量使用的话用 const
,若做为属性则使用readonly
。
额外的属性检查
可以漏写部分属性,但不能多写属性或者错写属性,即如果传入的对象中含有目标类型不存在的属性,TS 额外的属性检查机制会进行报错。
官方提供了绕开 TS 的额外属性检查的办法:
// 1. 使用类型断言
let mySquare = createSquare({width: 100, opacity: 0.5} as SquareConfig);
// 2. 声明接口时来一个任意类型,此时这个声明为该接口的对象,可以有任意数量、任意类型、只要名字不是 color 或 width 的属性
interface SquareConfig {
color?: string;
width?: number;
[propName: string]: any;
}
// 3. 更简单,只要把对象赋值到另一个变量上(抽离出来)即可:let squareOptions = {colour: "red", width: 100};
let mySquare = createSquare(squareOptions);
个人感觉不要使用,毕竟这个是 TS 来帮我们检查 BUG 的。
函数类型
使用接口表示函数类型
// 像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。interface SearchFunc {(source:string, subString:string):boolean
}
使用方法
let mySearch:SearchFunc = function(src:string,sub:string){let result = source.search(sub)
return result > -1
}
当然可以省略部分代码
let mySearch:SearchFunc = function(src,sub){let result = source.search(sub)
return result > -1
}
可索引类型
interface StringArray {[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
这个索引签名表示了当用 number
去索引 StringArray
时会得到 string
类型的返回值。
class Animal {name: string;}
class Dog extends Animal {breed: string;}
interface NotOkay {[x: number]: Animal;// 此处错误
[x: string]: Dog;
}
因为当使用 number
来索引时,JavaScript 会将它转换成 string
然后再去索引对象。也就是说用 100
(一个 number
)去索引等同于使用"100"
(一个string
)去索引。
记住一句话:数字类型索引的返回值必须是字符串类型索引返回值的子类型。这里 Animal
是Dog
的父类型,所以错误。
此外,字符串索引签名能够很好的描述 dictionary 模式,并且它们也会确保所有属性与其返回值类型相匹配。
如:
interface NumberDictionary {[index: string]: number;
length: number; // 可以,length 是 number 类型
name: string // 错误,`name` 的类型与索引类型返回值的类型不匹配
}
// 这里一知半解不太明白是什么意思
最后你还可以给索引签名设置为只读,防止了给索引赋值:
interface ReadonlyStringArray {readonly [index: number]: string;
}
类类型
这一部分等看了 TS 中的类以后再回头来看
继承接口
interface Shape {color: string;}
interface Square extends Shape {sideLength: number;}
let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
这样 square
的 2 个参数都要受限制了
一个接口可以继承多个接口,创建出多个接口的合成接口。
interface Shape {color: string;}
interface PenStroke {penWidth: number;}
interface Square extends Shape, PenStroke {sideLength: number;}
混合类型
这个也留着以后回头来看
接口继承类
同上