一、什么是接口
https://segmentfault.com/a/1190000010979494
接口的声明
在前面我们通过type可以用来声明一个对象类型
对象的另外一种声明方式就是通过接口来声明
type InfoType = {name: string;age: number;};interface IInfoType {name: string;age: number;}let p: InfoType = {name: 'cc',age: 79,};let p2: IInfoType = {name: 'bb',age: 90,};export {};
一般以I开头表明是个接口。
索引类型
使用interface来定义对象类型,这个时候其中的属性名、类型、方法都是确定的,但是有时候我们会遇到类似下面的对象
// 通过interface来定义索引类型interface IndexLanguage {[index: number]: string}const frontLanguage: IndexLanguage = {0: "HTML",1: "CSS",2: "JavaScript",3: "Vue"}interface ILanguageYear {[name: string]: number}const languageYear: ILanguageYear = {"C": 1972,"Java": 1995,"JavaScript": 1996,"TypeScript": 2014}
函数类型
前面我们都是通过interface来定义对象中普通的属性和方法的,实际上它也可以用来定义函数类型
// type CalcFn = (n1: number, n2: number) => number// 可调用的接口interface CalcFn {(n1: number, n2: number): number}function calc(num1: number, num2: number, calcFn: CalcFn) {return calcFn(num1, num2)}const add: CalcFn = (num1, num2) => {return num1 + num2}calc(20, 30, add)
除非特别的情况,还是推荐使用类型别名来定义函数
type CalcFn = (n1: number, n2: number) => number
交叉类型
我们学习了联合类型:
联合类型表示多个类型中一个即可
// 一种组合类型的方式: 联合类型type WhyType = number | stringtype Direction = "left" | "right" | "center"
还有另外一种类型合并,就是交叉类型(Intersection Types)
交叉类似表示需要满足多个类型的条件
交叉类型使用 & 符号
我们来看下面的交叉类型
// 另一种组件类型的方式: 交叉类型type WType = number & string
表达的含义是number和string要同时满足
但是有同时满足是一个number又是一个string的值吗?其实是没有的,所以MyType其实是一个never类型
在开发中,我们进行交叉时,通常是对对象类型进行交叉的
interface ISwim {swimming: () => void}interface IFly {flying: () => void}type MyType1 = ISwim | IFlytype MyType2 = ISwim & IFlyconst obj1: MyType1 = {flying() {}}const obj2: MyType2 = {swimming() {},flying() {}}
接口的继承
接口和类一样是可以进行继承的,也是使用extends关键字
并且接口是支持多继承的(类不支持多继承)
interface ISwim {swimming: () => void}interface IFly {flying: () => void}interface IAction extends ISwim, IFly {}const action: IAction = {swimming() {},flying() {}}
接口的实现
接口定义后,也是可以被类实现的
如果被一个类实现,那么在之后需要传入接口的地方,都可以将这个类传入
这就是面向接口开发
interface ISwim {swimming: () => void}interface IEat {eating: () => void}// 类实现接口class Animal {}// 继承: 只能实现单继承// 实现: 实现接口, 类可以实现多个接口class Fish extends Animal implements ISwim, IEat {swimming() {console.log("Fish Swmming")}eating() {console.log("Fish Eating")}}class Person implements ISwim {swimming() {console.log("Person Swimming")}}// 编写一些公共的API: 面向接口编程function swimAction(swimable: ISwim) {swimable.swimming()}// 1.所有实现了接口的类对应的对象, 都是可以传入swimAction(new Fish())swimAction(new Person())swimAction({swimming: function() {}})
interface和type的区别
我们会发现interface和type都可以用来定义对象类型,那么在开发中定义对象类型时,到底选择哪一个呢
如果是定义非对象类型
- 通常推荐使用type,比如Direction、Alignment、一些Function
如果是定义对象类型,那么他们是有区别
- interface 可以重复的对某个接口来定义属性和方法
- 而type定义的是别名,别名是不能重复的
// interface定义对象类型允许别名interface IFoo {name: string}interface IFoo {age: number}// 相当于合并const foo: IFoo = {name: "why",age: 18}// Window Document Date等类型是ts自己的库实现的// 所以interface Window{} 相当于合并 - - 不建议这么做document.getElementById("app") as HTMLDivElementwindow.addEventListenerinterface Window {age: number}window.age = 19console.log(window.age)//type定义对象类型不允许别名// type IBar = {// name: string// age: number// }// type IBar = {// }interface IPerson {}
字面量赋值
TypeScript在字面量直接赋值的过程中,为了进行类型推导会进行严格的类型限制
但是之后如果我们是将一个变量标识符赋值给其他的变量时,会进行freshness擦除操作
interface IPerson {name: stringage: numberheight: number}const info = {name: "why",age: 18,height: 1.88,address: "广州市"}// freshness擦除// 相当于把address这个类型擦擦除了const p: IPerson = infoconsole.log(info)console.log(p)// 应用function printInfo(person: IPerson) {console.log(person)}// 代码会报错 因为多了address类型// printInfo({// name: "why",// age: 18,// height: 1.88,// address: "广州市"// })const info1 = {name: "why",age: 18,height: 1.88,address: "广州市"}//这样可以 因为address被擦除printInfo(info1)
枚举类型
枚举类型是为数不多的TypeScript特性有的特性之一
枚举其实就是将一组可能出现的值,一个个列举出来,定义在一个类型中,这个类型就是枚举类型
枚举允许开发者定义一组命名常量,常量可以是数字、字符串类型
// type Direction = "left" | "Right" | "Top" | "Bottom"enum Direction {LEFT,RIGHT,TOP,BOTTOM}function turnDirection(direction: Direction) {switch (direction) {case Direction.LEFT:console.log("改变角色的方向向左")break;case Direction.RIGHT:console.log("改变角色的方向向右")break;case Direction.TOP:console.log("改变角色的方向向上")break;case Direction.BOTTOM:console.log("改变角色的方向向下")break;default:const foo: never = direction;break;}}turnDirection(Direction.LEFT)turnDirection(Direction.RIGHT)turnDirection(Direction.TOP)turnDirection(Direction.BOTTOM)
枚举类型的值
枚举类型默认是有值的,比如上面的枚举,默认值是 0 1 2 3 ….:
当然,我们也可以给枚举其他值:
也可以给他们赋值其他的类型:
// type Direction = "left" | "Right" | "Top" | "Bottom"enum Direction {LEFT = "LEFT",RIGHT = "RIGHT",TOP = "TOP",BOTTOM = "BOTTOM"}let name: string = "abc"let d: Direction = Direction.BOTTOMfunction turnDirection(direction: Direction) {console.log(direction)switch (direction) {case Direction.LEFT:console.log("改变角色的方向向左")break;case Direction.RIGHT:console.log("改变角色的方向向右")break;case Direction.TOP:console.log("改变角色的方向向上")break;case Direction.BOTTOM:console.log("改变角色的方向向下")break;default:const foo: never = direction;break;}}turnDirection(Direction.LEFT)turnDirection(Direction.RIGHT)turnDirection(Direction.TOP)turnDirection(Direction.BOTTOM)export {}
