首页 > 其他 > 详细

四、接口

时间:2019-09-07 01:02:08      阅读:90      评论:0      收藏:0      [点我收藏+]
介绍

定义原则:TS核心原则之一是对值所具有的结构进行类型检查,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约
定义:接口相当于类型检查器,检查属性是否满足接口定义的类型
注:类型检查器不会检查属性的顺序,只要相应的属性存在,并且类型正确就行

参数描述

对于传入参数可以包含很多属性,但编译器只会检查那些必须的属性是否存在,并且类型是否匹配

function commonFn(commonObj: { label: string }) {
  console.log(commonObj.label);
}
let commonParam = { size: 10, label: ‘this is a label‘ };
commonFn(commonParam);
// 用来描述对象的要求,描述有一个label属性且类型为string
interface interFn {
  label: string;
}
function interfaceFn(interfaceObj: interFn) {
  console.log(interfaceObj.label);
}
let interfaceParam = { size: 10, label: ‘this is interface label‘ };
interfaceFn(interfaceParam);

可选属性

概念:接口属性不全都是必须要传的,有些只有某些条件下存在,或者根本不存在
定义:需要在可选属性名后面加一个?
好处:

  • 可以对可能存在的属性进行预定义
  • 可以捕获引用了不存在的属性时的错误
interface SquareConfig {
  color?: string;
  width?: number;
  [size: number]: any;
}
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‘ });

注意事项:

  • 如果既定义了额外的属性,并且又定义了可选属性,那么TS会认为这段代码可能存在bug
  • 对象字面量会被特殊对待而且会经过额外属性检查,如果一个对象字面量存在任何目标类型不包含的属性时,会报错
例如: let mySquare1 = createSquare({ color: ‘black‘, size: 10 });

处理:

  • 一种最简单的处理方式是使用断言
  • 另一种是添加一个字符串索引签名,前提是能够确定这个对象具有某些作为特殊用途使用的额外属性
  • 将这个对象赋值给另外一个变量,因为新的变量不会经过额外属性检查
    注:大部分情况下不需要绕开这些检查,除非使用一些复杂的字面量,大部分情况下检查出来的可能是真正的bug,如果额外检查出了错误,应该修改接口定义适配目前的类型
interface SquareConfig {
  color?: string;
  width?: number;
  [size: number]: any;
}
let mySquare1 = createSquare({ color: ‘black‘, size: 10 } as SquareConfig);
let squareOptions = { color: ‘black‘, size: 10 };
let mySquare2 = createSquare(squareOptions);
console.log(mySquare2);

只读属性

针对有些对象只能在对象刚刚创建的时候修改其值
定义:在属性名前使用readonly关键字指定

interface Point {
  readonly x: number;
  readonly y: number;
}
// 初始化赋值
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error 无法进行二次赋值
// 创建一个只读数组
let arrA: number[] = [1,2,3,4];
let arrOnly: ReadonlyArray<number> = arrA;
arrOnly[0] = 12; // error
arrOnly.push(10); // error
arrOnly.length = 100; // error
arrA = arrOnly; // error
// 即使把arrOnly赋值给普通数组也是不允许的,不过可以用断言重写
arrA = arrOnly as number[];

函数类型

接口出了描述带有属性的普通对象外,也可以描述函数类型
定义:为了使接口表示函数类型,需要给接口定义一个调用签名,是一个只有参数列表和返回值类型的函数定义,参数列表里面的每个参数都需要名字和类型

// 表示函数接收两个字符串类型的参数,并且返回布尔类型值
interface SearchFunc {
  (source: string, subString: string): boolean;
}

实现接口时:

  • 函数的参数会逐个进行类型检查,要求对应位置上的参数类型是兼容的
  • 参数如果不想指定类型,TS会自己推断参数类型
  • 函数的返回值类型必须与接口期望的返回值相同
// 使用这个接口
let mySearch: SearchFunc;
// 将同类型的函数赋值给接口变量
mySearch = function(source: string, subString: string) {
  let result = source.search(subString);
  return result > -1;
}
// 对于函数的类型检查来说,函数的参数名不需要与接口的名字相匹配
let meSearch: SearchFunc;
meSearch = function(src: string, sub: string): boolean {
  let result = src.search(sub);
  return result > 1;
}

可索引的类型

与接口描述函数类型差不多,可以可以描述那些通过索引得到的类型:a[10]
索引类型定义:具有一个索引签名,描述了对象索引的类型,还有相应的索引返回值类型
TS支持两种索引签名,可以同时使用两种类型的索引:

  • 字符串
  • 数字索引:签名的返回值必须是字符串索引返回值类型的子类型(因为使用number来索引时,JS会将他转换成string然后再去索引对象)
  • 同时拥有string和number类型的索引签名(不常用)
// 接口描述了索引类型以及索引值的类型
interface StringArray {
  [index: number]: string;
}
let myArray: StringArray;
myArray = [‘Bob‘, ‘Fred‘];
let myStr: string = myArray[0];

场景:如果想确认存储在对象中任何内容都符合 { name: string } 的结构可以通过设置:[index: string]: { name: string } 实现,这样的话在此对象中所有属性类型都需要统一成name属性格式

注:所有成员都必须符合字符串的索引签名:当声明一个索引签名时,所有明确的成员都必须符合索引签名

使用一组有限的字符串字面量:一个索引签名可以通过映射来使索引字符串为联合类型中的一员

class Animal {
  name: string;
}
class Dog extends Animal {
  breed: string;
}
interface NotOkay {
  [x: number]: Animal; // error
  [x: string]: Dog
}
// 当声明一个索引签名时,所有明确的成员都必须符合索引签名
interface NumberDictionary {
  [index: string]: number;
  length: number;
  name: string; // error 属性值必须为number
}

// 使用一组有限的字符串字面量
type Index = ‘a‘ | ‘b‘ | ‘c‘;
type FromIndex = { [k in Index] ? : number };
const good: FromIndex = { b: 1, c: 2 };
// { b: 1, c: 2, d: 3 }` 不能分配给 ‘FromIndex
// 对象字面量只能指定已知类型,‘d‘ 不存在 ‘FromIndex‘ 类型上
const bad: FromIndex = { b: 1, c: 2, d: 3 };
// 同时拥有 string 和 number 类型的索引签名 这样的话接口中就可以定义字符串跟number两种类型了
interface ArrStr {
  [key: string]: string | number; // 必须包括所有成员类型
  [index: number]: string; // 字符串索引类型的子级
  length: number;
  name: string;
}

// 可以将索引签名设置为只读,防止给索引赋值
interface ReadonlyStringArray {
  readonly [index: number]: string;
}
let onlyArray: ReadonlyStringArray = [‘Alice‘, ‘Bob‘];
onlyArray[2] = ‘Mallory‘; // error 只读无法重新赋值

类类型

实现接口:与C#跟Java一样,TS也能够用接口来明确的强制一个类去符合某种契约
接口描述了类的公共部分,而不是公共和私有两部分,不会检查类是否有某些私有成员
一个类实现一个接口时,只对其实例部分进行类型检查,constructor存在于类的静态部分,所以不在检查范围内
类的类型:

  • 静态部分的类型
  • 实例的类型
// 基本用法
interface ClockInterface {
  currentTime: Date;
  setTime(d: Date);
}
class Clock implements ClockInterface {
  currentTime: Date;
  setTime(d: Date) {
    this.currentTime = d;
  }
  constructor(h: number, m: number){}
}
// 为构造函数所用
interface ClockConstructor {
  new (hour: number, minute: number): ClockInterf;
}
// 为实例方法所用
interface ClockInterf {
  tick();
}
// 使用传入的类型创建实例
function createClock(ctor: ClockConstructor, hour: number, minute: number): ClockInterf{
  return new ctor(hour, minute);
}
class DigitalClock implements ClockInterf {
  constructor(h: number, m: number){}
  tick() {
    console.log(‘beep beep‘);
  }
}
class AnalogClock implements ClockInterf{
  constructor(h: number, m: number){}
  tick() {
    console.log(‘tick tock‘);
  }
}
let digital = createClock(DigitalClock, 12,17);
let analog = createClock(AnalogClock, 7, 32);
console.log(digital)

接口继承

可以从一个接口中复制成员到另一个接口中,可以灵活的将接口分割到可重用的模块里

interface Shape {
  color: string;
}
interface Square extends Shape {
  sideLength: number;
}
let square = <Square>{};
square.color = ‘blue‘;
square.sideLength = 10;
console.log(square);

一个接口可以继承多个接口,创建多个接口的合成接口

interface Shape1 {
  color: string;
}
interface PenStroke {
  penWidth: number;
}
interface Square1 extends Shape1, PenStroke {
  sideLength: number;
}
let square1 = <Square1>{};
square1.color = ‘blue‘;
square1.sideLength = 20;
square1.penWidth = 5.0;
console.log(square1);

混合类型

一个对象可以同时具有多种类型(可以同时作为函数和对象使用,并带有额外属性)

interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}
function getCounter(): Counter {
  let counter = <Counter>function(start: number){
    console.log(start);
  };
  counter.interval = 23;
  counter.reset = function(){
    counter.interval = 123;
    console.log(counter.interval);
  }
  return counter;
}
let amount = getCounter();
amount(10);
amount.reset();
amount.interval = 5;

接口继承类

接口继承了一个类类型时,他会继承类的成员但不包括其实现,就像接口声明了所有类中存在的成员,但并没有提供具体实现一样,接口同样会继承到类的private和protected成员

class Control {
  private state: any;
}
// 继承了Control类包含了Control的所有成员,包括私有的state
interface SelectableControl extends Control{
  select(): void;
}
// 由于state是私有成员,所以只能够Control的子类们才能实现SelectableControl接口
class Button extends Control implements SelectableControl {
  select() {}
}
class TextBox extends Control{
  select() {}
}
// Image类没有继承Control所以无法实现接口SelectableControl
class Image implements SelectableControl {
  select() {}
}

四、接口

原文:https://blog.51cto.com/14533658/2436001

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!