TS变量与接口
变量声明
其实TS中的变量声明和JS中是一样的,所以你会JS就会TS,无外乎var、let和const,记住以下的表格内容就能解决绝大多数场景问题。
接口在面向对象语言中,接口是个比较核心的概念,其作用就是对类型命名和代码定义契约,其实就是对行为的抽象,对对象的形状进行描述。在TS中就是对值所具有的结构进行类型检查。
- // 原始方法
- function printLabel(labelObj: {label:string}){
- console.log(labelObj.label);
- }
- let myObj = {name:"wenbo",label:"size 110"};
- printLabel(myObj);
- // 接口方法
- interface LabelValue {
- label: string;
- }
- function printLabel2(labelValue:LabelValue){
- console.log(labelValue.label);
- }
- printLabel2(myObj);
上述代码表明,printLabel中传入对象labelObj有string类型的label属性。而传入的对象参数实际会包含很多属性,但是编译器智慧检查那些必须的属性是否存在、类型是否匹配。printLabel2接口其实就是对printLabel中传入对象类型的抽象,定义它的参数行为,类型检查器不会关注属性的顺序。
接口对象的声明方式
接口对象的声明方式很简单,就是在接口名前加上interface即可。
- interface myObj {
- name:string;
- label:string;
- }
接口对象的基本属性
接口对象的属性无外乎默认属性、可选属性和只读属性等。
- 默认属性:表示该属性必须存在,可读可改
- 可选属性:表示该属性可有可无、可读可改,只需要在属性名后加上?符号即可,如:name?:string;。可以对可能存在的属性进行预定义,捕获引用了不存在属性时的错误。
- 只读属性:表示该属性只能读取、不可修改,只需要在对象创建时对指定属性名前加上readonly即可,可以确保创建后不被修改。
- interface LabelValue{
- readonly id: number;//只读属性,表示该属性只能读取、不可修改
- name?: string;//可选属性,表示该属性可有可无
- label: string;//默认属性,表示该属性必须存在
- }
- function printLabel(labelValue:LabelValue){
- console.log(labelValue);
- }
- let myObj: LabelValue = {name:"yichuan",id:100,label:"size 110"};
- printLabel(myObj);//{ name: 'yichuan', id: 100, label: 'size 110' }
- myObj.id = 200;// 报错: Cannot assign to 'id' because it is a constant or a read-only property.
- let myObj2: LabelValue ={id:100};
- // Type '{ id: number}' is not assignable to type 'LabelValue'.
- // Property 'label' is missing in type '{ id: number}'.
- // 报错: 缺少 label 属性
接口对象的函数类型
接口能够描述JavaScript中对象拥有的各种各样的外形。 除了描述带有属性的普通对象外,接口也可以描述函数类型。
使用接口表示函数类型,需要给接口定义一个调用签名,是一个只有参数列表和返回值类型的函数,其中参数列表的每个参数都得有属性名和类型。
- interface myFun{
- (name:string, age:number): void;//()中的是函数类型,void是否有返回值
- }
- let iFun: myFun;
- iFun = function (name:string,age:number){
- console.log(`my name is ${name}, my age is ${age}`)
- }
- iFun("yichuan",18);//my name is yichuan, my age is 18
我们可以看到:首先创建了一个函数类型的接口myFun,再创建了一个函数类型的变量iFun,并将同类型的函数赋值给这个变量。
对于函数类型检查而言,函数的参数名不需要与接口定义的名字匹配。类型检查器会对函数参数进行逐个检查,判断对应位置的参数类型是否匹配。当然,如果你在函数中没有指定参数类型,那么TS类型系统会根据接口进行推断,并执行检查是否匹配。
- interface myFun{
- (name:string, age:number): void;
- }
- let iFun: myFun;
- iFun = function (name,age){
- console.log(`my name is ${name}, my age is ${age}`)
- }
- iFun("yichuan",18);////my name is yichuan, my age is 18
接口对象的可索引类型
与使用接口描述函数类型差不多,只不过可索引类型时通过描述对象索引类型和索引返回值类型的「索引签名」。
- //定义一个学生列表接口
- interface StudentList{
- id: number;
- name: string;
- }
- // 定义一个班级接口
- interface ClassList{
- classname: string;
- students: StudentList[];
- [index: string]: any;//可以用任意的string类型去索引声明的对象, 值是any类型
- }
- function printLabel(data:ClassList){
- return data;
- }
- printLabel({
- classname:"class 1",
- numbers:30,
- students:[{
- id:2001,
- name:"yichuan"
- }]
- })
可索引接口的类型只可以使用string和number进行定义索引签名。可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。当使用 number来索引时,JavaScript会将它转换成string然后再去索引对象。
- 字符串定义索引签名
- 数字定义索引签名
- 混合类型定义索引签名
- class Animal {
- name: string;
- }
- class Dog extends Animal {
- breed: string;
- }
- // 错误:使用数值型的字符串索引,有时会得到完全不同的Animal!
- interface NotOkay {
- [x: number]: Animal;
- [x: string]: Dog;
- }
字符串索引签名能够很好的描述dictionary模式,并且它们也会确保所有属性与其返回值类型相匹配。因为字符串索引声明了obj.property和obj["property"]两种形式都可以。
为防止给索引赋值,可以将其索引签名定义为只读类型。
- interface ReadonlyStringArray {
- readonly [index: number]: string;
- }
- let myArray: ReadonlyStringArray = ["Alice", "Bob"];
- myArray[2] = "Mallory"; // error!
类接口
TS中可以对类设置强制执行的类型约定,即类接口。
- interface FatherInterface{
- firstName: string;
- }
- class Son implements FatherInterface{
- firstName!: String;
- constructor(lastName:string,age:number){};
- }
注意:
- 接口只描述类的公共部分,而不是公共和私有两部分。它不会帮你检查类是否具有某些私有成员。
- 类实现接口时,必须实现接口所有的属性
- 接口无法约束类的构造函数,类型检查器只会对实例部分进行检查
我们知道类具有两种类型:静态部分的类型和实例的类型。当你用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误:只对其实例部分进行类型检查,而constructor存在于类的静态部分,所以不在检查的范围内。
- interface FatherInterface{
- new (firstName:string);
- }
- class Son implements FatherInterface{
- constructor(firstName:string,lastName:string,age:number){};
- }
继承接口
和类一样,接口也可以相互继承。可以将一个接口成员复制到另一个接口,灵活地分割接口到可复用模块中。
- interface DogInterface{
- type:string;
- }
- interface Erha extends DogInterface{
- name:string;
- age:number;
- }
- let erha = <Erha>{};
- erha.type = "dog";
- erha.name = "bobo";
- erha.age = 2;
同样的,接口也可以实现多继承。
- class Son implements FatherInterface{
- constructor(firstName:string,lastName:string,age:number){};
- }
- interface AnimalInterface{
- foal:string;
- }
- interface DogInterface{
- type:string;
- }
- interface Erha extends DogInterface, AnimalInterface{
- name:string;
- age:number;
- }
- let erha = <Erha>{};
- erha.type = "dog";
- erha.name = "bobo";
- erha.age = 2;
- erha.foal = "分娩";
小结
interface接口的定义其实很简单,和定义对象一样的形式。接口对象的基本属性包括:默认属性、可选属性以及只读属性,其可索引类型的定义只有string和number两种形式,类接口进行继承的形式和类的继承大同小异。
参考文章
- 阿宝哥的《重学TS》
- 《ts中文文档》
- 《大话 Typescript 接口》
本文转载自微信公众号「前端万有引力」,可以通过以下二维码关注。转载本文请联系前端万有引力公众号。