3.TS接口


TS接口

接口 定义一个类型,以 约束变量约束类,

类型检查器会以接口类型为标准对值的结构进行类型检查.

使用场景: 抽象的,描述形状的,extends,implements

“鸭式辨型法”:如果它走起 路来像鸭子,叫起来也是鸭子,那么它就是鸭子

一个类可以 继承另一个类 并实现多个接口
一个类可以 实现多个接口 ,一个接口也可以 被多个类实现
一个类可以只能 继承一个父类 , 可以 被多个子类继承

变量后面:xxx可能是,type定义的类型,可能是class定义的类,

可能是 interface 定义的接口,可能是 字面量,可能是 js原始类型

可选属性

interface SquareConfig {
  color?: string;
  width?: number;
}

任意属性

//无法预先知道有哪些新的属性的时候,可以使用 `[propName:string]:any`,propName名字是任意的
interface SquareConfig {
    color: string;
    width: number;
    // 对象的key 可以有三种类型 string number symbol
    [propName: string]: any;
}

只读属性

// 只读属性,只在创建的时候可以赋值
interface Point {
    readonly x: number;
    readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
// 变量用const,属性用readonly

断言后可继续加属性

interface SquareConfig {
    color: string;
    width: number;
}
const a: SquareConfig = {
    color: 'red',
    width: 1,
    name: 'square' // 有了as可以多属性而不报错,无as报错
} as SquareConfig

同名接口会被合并

interface SquareConfig {
    color: string;
    width: number;
}
interface SquareConfig {
    name: string
}
const a:SquareConfig = {
    color: 'red',
    width: 1,
    name: 'square'
}
// 继承 也是类似的效果, 
// 三个属性都写 SquareConfig 里也是类似的效果

接口继承,约束类

类 实现 接口

interface Speakable{ // 代表具有speak函数的对象
    speak():void;
}
interface Eatable{ // 代表具有eat函数的对象
    eat():void
}
//一个类可以实现多个接口,继承后必须满足接口的条件(实现接口)
class Person implements Speakable,Eatable{
    speak(){
        console.log('Person说话');
    }
    eat(){}
}

接口 继承 另一个接口

interface Speakable {
    speak(): void
}
interface SpeakChinese extends Speakable {
    speakChinese(): void
}
class Person implements SpeakChinese {
    speak() {
        console.log('Person')
    }
    speakChinese() {
        console.log('speakChinese')
    }
}

接口 继承 多个接口

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

interface Square extends Shape, PenStroke {
    sideLength: number;
}

let square = <Square>{}; // Square类型的对象
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

约束变量 为 对象类型

// 约束变量 接收的值 必须为对象
interface Speakable{
    speak():void;
    name:string;
}
let speakMan:Speakable = {
    speak(){},//少属性会报错
    name,
    age//多属性也会报错
}

约束变量 为 函数类型

约束 变量 的值为 特定类型函数

对于约束 函数变量 的值时,值(函数) 的参数名不需要和接口中一致,

只要求 对应位置上的参数类型是兼容的

interface SearchFunc {
  (source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
// 下面的src sub 不指定类型也会根据function内进行类型推断
mySearch = function(src: string, sub: string): boolean {
  let result = src.search(sub);
  return result > -1;
}

对象字面量会经历额外属性检查

对象字面量 直接作为函数参数时 会经历 额外属性检查

将对象赋值给变量 再作为参数 传入函数调用,则不会 检查变量对象上的额外属性

interface SquareConfig {
    height: number;
    width: number;
}
function createSquare(config: SquareConfig):void {}
// 报错error: 'color' not expected in type 'SquareConfig'
// 因为直接传入了 对象字面量,所以经历了额外属性检查,
// 不允许拥有接口定义外的额外属性
let mySquare = createSquare({height:100, width: 100,color: "red"});
// 解决方案1,不适用 对象字面量,改写成对象 再作为参数传入
let squareOptions = { color: "red", width: 100 };
let mySquare = createSquare(squareOptions);
// 解决方案2,在 interface SquareConfig 中增加 [propName: string]: any;

约束 类 的 属性

类 实现(implements) 接口,即完成 接口 对类的约束

创建一个类可代表两个类型,静态部分的类型,实例部分的类型

interface Speakable {
    name: string;               //约束具有某属性
    speak(words: string): void  //约束具有某方法 
    // speak: (words: string) => void // 也可以这样写
}
class Dog implements Speakable {
    name!: string;
    speak(words:string) {
        console.log(words);
    }
}
let dog = new Dog();
dog.speak('汪汪汪');

但上面的接口Speakable 只约束的是 类的定义(静态类型)

约束 类 的 构造函数

不加new是约束变量为函数的,加new是 约束 类 的 构造函数的

// interface SearchFunc {
//   (source: string, subString: string): boolean;
// }
interface WithNameClass {
    new(name: string): Animalzhen
}
class Animal {
    constructor(public name: string) {
    }
}
// 约束clazz这个对象的构造函数必须带name
function createAnimal(clazz: WithNameClass, name: string) {
    return new clazz(name);
}
let a = createAnimal(Animal, 'zhufeng');
console.log(a.name);

如果这样写会报错,因为此时的Person 描述的是实例,clazz被认为应该是某种对象

下面改成createInstance(clazz: typeof Person)就可以

class Person{}
function createInstance(clazz: Person) { // clazz被认为应该是对象
    return new clazz()
    // error: 此表达式不可构造。类型 "Person" 没有构造签名。
}
let instance = createInstance(Person)

约束 变量 为 混合类型

写法为匿名函数的样子,如果写了函数名就变成描述类了

// 约束一个函数,同时这个函数还作为对象加了两个属性
interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}
function getCounter(): Counter {
    let counter = <Counter>function (start: number) { };
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}

let c = getCounter();

约束 变量 的 索引

对数组和对象进行约束

// 表示index必须为number,值必须为string
interface UserInterface {
  [index:number]:string
}
let arr:UserInterface = ['lzy1','lzy2'];
interface NumberDictionary {
  [index: string]: number;
  length: number;
  name: string       // 错误,必须全为number
}

通过索引访问符来访问接口中的属性类型(*)

interface Person {
  name: string;
  age: string;
  address: {
    num: 316;
  };
  // [key: string]: any;  如果写了任意类型,则去出的val 就是任意类型
}
type PersonName = Person["name"];
type PersonNum = Person["address"]["num"]; // 316
type PropTypeUnion = keyof Person; // 取key name | age | address
type PropTypeValueUnion = Person[keyof Person]; // 取值 string | {num:316}

文章作者: 罗紫宇
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 罗紫宇 !
  目录