1. 接口的声明
在前面我们通过type可以用来声明一个对象类型:
type Point = {
x: number
y: number
}
对象的另外一种声明方式就是通过接口来声明:
interface Point = {
x: number
y: number
}
接口类型也可以定义可选类型和只读属性,只读属性指我们再初始化之后,这个值是不可以被修改的
// 通过类型(type)别名来声明对象类型
// type InfoType = {name: string, age: number}
// 另外一种方式声明对象类型: 接口interface
// 在其中可以定义可选类型
// 也可以定义只读属性
interface IInfoType {
readonly name: string
age: number
friend?: {
name: string
}
}
const info: IInfoType = {
name: "why",
age: 18,
friend: {
name: "kobe"
}
}
console.log(info.friend?.name)
console.log(info.name)
// info.name = "123"
info.age = 20
2. 索引类型
// 通过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
}
3. 函数类型
前面我们都是通过interface来定义对象中普通的属性和方法的,实际上它也可以用来定义函数类型:
用接口定义函数类型
image.png
// 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)
4. 接口继承
接口和类一样是可以进行继承的,也是使用extends关键字:
并且我们会发现,接口是支持多继承的(类不支持多继承)
interface ISwim {
swimming: () => void;
}
interface IFly {
flying: () => void;
}
interface IAction extends ISwim, IFly {}
//支持多继承
const action: IAction = {
swimming() {},
flying() {},
//里面必须有这两个方法
};
image.png
5.交叉类型
image.png6.交叉类型的应用
image.png// 一种组合类型的方式: 联合类型
type WhyType = number | string;
type Direction = "left" | "right" | "center";
/* ,联合类型的话是把我们多个这个值,
多个我们的类型,多个类型跟他联合在一起,到时候的话,你取里面的任何,一个类型都可以 */
// 另一种组件类型的方式: 交叉类型
type WType = number & string;
interface ISwim {
swimming: () => void;
}
interface IFly {
flying: () => void;
}
type MyType1 = ISwim | IFly;
type MyType2 = ISwim & IFly;
const obj1: MyType1 = {
flying() {},
};
const obj2: MyType2 = {
swimming() {},
flying() {},
};
export {};
两个接口他们的组合的时候就有两种方式了,第一种方式的话是定义一个新的接口,然后让他们继承自我原来的这两个接口,那么这个接口里面就相当于把前面这两个就给他组合在一起了,另外一种方式,交叉类型,他也是可以把我们两个对象类型,两个对象类型跟他结合在一起,结合在一起之后的话,生成一个新的类型.
7.接口的实现
1.接口作为标识符的使用
interface Iswim {
swimming: () => void;
}
interface IEat {
eating: () => void;
}
//这两个接口可以作为类型,比如在定义变量的时候
const a: Iswim = {
swimming() {},
}; //接口作为标识符类型
function foo(swim: Iswim) {
//传入的这个对象必须满足这个接口
}
foo({ swimming() {} });
- 类可以作为接口
interface Iswim {
swimming: () => void;
}
interface IEat {
eating: () => void;
}
// 类实现接口
class Animal {}
//类Fish除了可以继承Animal,也可以实现接口
//但是继承的化只能实现单继承
class Fish extends Animal implements ISwim, IEat {
swimming() {
console.log("Fish Swmming");
}
eating() {
console.log("Fish Eating");
}
//因为实现了两个接口所以里面必须有swiming和eating
/*
使用类实现接口的化,编写一一些公共的公共的API的话,在很多的情况下都可以调用的,对于可复用的一些API的时候,它就变得更加的灵活。那么我们就可以面向接口进行编程。
*/
}
//// 编写一些公共的API: 面向接口编程
//function swimAction(swimable: Fish {
// swimable.swimming();
//}
swimAction(new Fish());
//这么写的话,这个函数就不具备通用性,因为这个参数我们就只能传Fish类型的一个对象。
//如果我们想swimAction({swimming: function() {}})是不可以的。只能new一个Fish对象
//这个时候我们就可以面向接口编程
function swimAction(swimable: ISwim) {
swimable.swimming()
}
// 1.所有实现了接口的类对应的对象, 都是可以传入
swimAction(new Fish())
//如果Person也实现了 ISwim
class Person implements ISwim {
swimming() {
console.log("Person Swimming")
}
}
swimAction(new Person())
swimAction({swimming: function() {}})
/* 就意味着只要是有一个类,它实现了这个接口,到时候的话,它都是可以调动我公共的这个API的,那么我在编辑这个公共API的时候,我这里最好就是面向接口编程的
面向接口编程的话,就让我编写的这个API他更加具备通用性啦,在很多很多地方都可以被别人调用,只要你实现了我的这个接口,甚至是我们普通的对象字面量,你只要也是符合我接口对应的类型的,你也是可以传到我们这个里面去的,可以转到这个里面去,所以这个的话就叫做面向接口编程
*/
export {};
8.interface和type区别
image.pnginterface IFoo {
name: string;
}
interface IFoo {
age: number;
}
/* interface允许定义两个名字相同的接口,意味着会把两个接口的内容合并在一起,相当于
interface Ifoo {
ame: string
age: number
}
*/
const foo: IFoo = {
name: "why",
age: 18,
};
export {};
// document.getElementById("app") as HTMLDivElement
/*
刚才我们看到的Windows类型,包括document这些类型它来自哪里呢,这些类型来自于我们在安装typescript的时候,它会默认帮助我们安装一些lib的库,
那安装的这些库里面,它就默认有帮助我们定义很多的类型,
包括比如说这个javascript里面内置的一些类型项,Math,Data这些类型,包括这些内置类型,
也包括dom的一些类型的API的话,
Winodw类型,HTMLDivElement类型等
06:01
*/
//window这个类型没有age这个属性,我们如果想有age这个属性的化
interface Window {
age: number;
}
//这时候不能覆盖以前的Window,而是合并
window.age = 19;
console.log(window.age);
//type根本不允许定义两个相同的类型
// type IBar = {
// name: string
// age: number
// }
// type IBar = {
// }
9.字面量赋值
interface IPerson {
name: string;
age: number;
height: number;
}
const info: IPerson = {
name: "why",
age: 18,
height: 1.88,
//address: "广州市", //不能添加这个属性,因为IPerson没有定义
};
怎么让他不报错呢
interface IPerson {
name: string;
age: number;
height: number;
}
const info = {
name: "why",
age: 18,
height: 1.88,
address: "广州市",
}; //info会做类型推导,推导出来
/*
const info: {
name: string;
age: number;
height: number;
address: string;
}
*/
const p: IPerson = info;
/*
这个时候把info类型和IPerson类型是冲突的,因为多了个address
ts主要做类型检测,类型检测通过,你这个代码就没有问题
fressness擦除
当把info这个引用类型赋值给p的时候,他会把我们对应某一个属性给它擦除掉,擦出掉之后,也就是address,如果依然是满足你这里的这个类型的情况下,也就是 IPerson ,因为我们这三个是不是依然是满足的,依然是满足的情况下,就是可以赋值的。如果info里面没有age,你再把这个address擦掉之后,你是少一个的,少一个是不行的,同学们就是擦除掉那些多余的,擦掉那些多余的之后,
我依然是符合你这类型的,他是允许我们这么赋值的,它允许这么赋值
*/
console.log(info);
console.log(p);
/* { name: 'why', age: 18, height: 1.88, address: '广州市' }
{ name: 'why', age: 18, height: 1.88, address: '广州市' } */
export {};
这是因为TypeScript在字面量直接赋值的过程中,为了进行类型推导会进行严格的类型限制。
但是之后如果我们是将一个 变量标识符 赋值给其他的变量时,会进行freshness擦除操作。
应用
interface IPerson {
name: string;
age: number;
height: number;
}
function printInfo(person: IPerson) {
console.log(person);
// console.log(person.address);不可以使用address,因为对于类型检测来说,,人家认为你这个person里面只有三个属性,,在这里取address的时候,在类型检测这一步他是过不去的,但是可以直接打印所有
}
// 代码会报错
// printInfo({
// name: "why",
// age: 18,
// height: 1.88,
// address: "广州市"
// })
const info = {
name: "why",
age: 18,
height: 1.88,
address: "广州市",
};
printInfo(info);
//{ name: 'why', age: 18, height: 1.88, address: '广州市' }
export {};
10.TypeScript枚举类型
image.png//,其实枚举类型就是定义一组常量,并且给他放到我们对应的一种数据类型里面
// type Direction = "left" | "Right" | "Top" | "Bottom"
enum Direction {
LEFT,
RIGHT,
TOP,
BOTTOM,
} //代码阅读性强
/*
看起来这些东西是我们的一些标识的一些常量,但是其实本质上他们是一些数字常量,也就第一个的话其实是一个零,然后第二个的话是一个一,第三个的话是一个二,然后第四个的话就是一个三,它内部是由我们这里这么一些数字的值的,只不过这些值你不需要去写,你不写的时候,他们默认也是有的,但是能改变这里这些东西的值
enum Direction {
LEFT = 0,
RIGH = 1,
TOP = 2,
BOTTOM = 3,
} 默认
enum Direction {
LEFT = 100,
RIGH = 200,
TOP = 300,
BOTTOM = 400,
}
enum Direction {
LEFT = 100,
RIGH ,//101
TOP ,//102
BOTTOM,//103
}
*/
function 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; //如果少些一个case的化,代码会报错。因为default用了never类型
default:
const foo: never = direction;
break;
}
}
turnDirection(Direction.LEFT);
turnDirection(Direction.RIGHT);
turnDirection(Direction.TOP);
turnDirection(Direction.BOTTOM);
export {};
/*
0
改变角色的方向向左
1
改变角色的方向向右
2
改变角色的方向向上
3
改变角色的方向向下
*/
网友评论