TypeScript 接口

本文深入探讨了TypeScript中的接口特性,包括对值的结构进行类型检查、可选属性允许预定义可能存在的属性、只读属性确保对象创建后属性不可修改、字符串索引签名定义额外属性以及如何通过接口约束函数和类类型。此外,还介绍了如何处理对象字面量的额外属性检查以及如何通过类型断言绕过这种检查。
摘要由CSDN通过智能技术生成

接口

这篇文章是对文档的整理

TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 而接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

printLabel有一个参数,并要求这个对象参数有一个名为label类型为string的属性。

需要注意的是,我们传入的对象参数实际上会包含很多属性,但是编译器只会检查那些必需的属性是否存在,并且其类型是否匹配。 然而,有些时候TypeScript却并不会这么宽松,我们下面会稍做讲解。

类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。

总结:

定义 接口的类型(即通过接口实现类型) 的变量 必须完全符合对应类型的属性和方法

函数中对传入参数的类型检查,只检查必需的属性,多或少无所谓

可选属性

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

带有可选属性的接口与普通的接口定义差不多,只是在可选属性名字定义的后面加一个?符号

好处:

  • 可以对可能存在的属性进行预定义。
  • 可以捕获引用了不存在的属性时的错误。

例子:

我们故意将createSquare里的color属性名拼错,就会得到一个错误提示:

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

function createSquare(config: SquareConfig): { color: string; area: number } {
  let newSquare = {color: "white", area: 100};
  if (config.color) {
    // Error: Property 'clor' does not exist on type 'SquareConfig'
    newSquare.color = config.clor;
  }
  if (config.width) {
    newSquare.area = config.width * config.width;
  }
  return newSquare;
}

let mySquare = createSquare({color: "black"});

只读属性

一些对象属性只能在对象刚刚创建的时候修改其值。 你可以在属性名前用readonly来指定只读属性:

interface Point {
    readonly x: number;
    readonly y: number;
}

TypeScript具有ReadonlyArray<T>类型,它与Array<T>相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!

上面代码的最后一行,可以看到就算把整个ReadonlyArray赋值到一个普通数组也是不可以的。 但是你可以用类型断言重写

a = ro as number[];

readonly 与 const

最简单判断该用readonly还是const的方法:

看要把它做为变量使用还是做为一个属性。

做为变量使用的话用const,若做为属性则使用readonly

对象字面量会被特殊对待而且会经过额外属性检查,当将它们赋值给变量或作为参数传递的时候。 如果一个对象字面量存在任何“目标类型”不包含的属性时,你会得到一个错误。

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

function createSquare(config: SquareConfig): { color: string; area: number } {
    // ...
}

let mySquare = createSquare({ colour: "red", width: 100 });

所以会以下出现:error: 'colour' not expected in type 'SquareConfig'

绕过属性额外检查:

  1. 使用类型断言

    interface SquareConfig {
      color?:string,
      width:number
    }
    
    function createSquare(config:SquareConfig) {
      return config
    }
    
    let mySquare = createSquare({colour:"red",width:40} as SquareConfig)
    console.log(mySquare)
    
  2. 添加一个字符串索引签名

    interface SquareConfig {
        color?: string;
        width?: number;
        [propName: string]: any;
    }
    

    前提:能够确定这个对象可能具有某些做为特殊用途使用的额外属性

可索引类型

与使用接口描述函数类型差不多,我们也可以描述那些能够“通过索引得到”的类型,比如a[10]ageMap["daniel"]。 可索引类型具有一个索引签名,它描述了对象索引的类型,还有相应的索引返回值类型。 让我们看一个例子:

interface StringArray {
  [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

上面例子里,我们定义了StringArray接口,它具有索引签名。 这个索引签名表示了当用number去索引StringArray时会得到string类型的返回值。

共有支持两种索引签名:字符串和数字。

可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。

数字索引可能出现的问题

这是因为当使用number来索引时,JavaScript会将它转换成string然后再去索引对象。 也就是说用100(一个number)去索引等同于使用"100"(一个string)去索引,因此两者需要保持一致。

class Animal {
    name: string;
}
class Dog extends Animal {
    breed: string;
}

// 错误:使用'string'索引,有时会得到Animal!
interface NotOkay {
    [x: number]: Animal;
    [x: string]: Dog;
}

它们也会确保所有属性与其返回值类型相匹配。 因为字符串索引声明了obj.propertyobj["property"]两种形式都可以。

interface NumberDictionary {
  [index: string]: number;
  length: number;    // 可以,length是number类型
  name: string       // 错误,`name`的类型与索引类型返回值的类型不匹配
}

这是因为在接口的设置中,设置的索引标签为字符串,最后输出数字类型

但是,name属性是一个字符串类型,不能通过索引得到


你可以将索引签名设置为只读,这样就防止了给索引赋值:

interface ReadonlyStringArray {
    readonly [index: number]: string;
}
let myArray: ReadonlyStringArray = ["Alice", "Bob"];
myArray[2] = "Mallory"; // error!

你不能设置myArray[2],因为索引签名是只读的。

函数类型

interface Girl {
    say():string
}

定义的接口中有函数类型,并且函数的返回值是一个字符串

类 类型

TypeScript也能够用它来明确的强制一个类去符合某种契约——简而言之,就是接口中存在的属性或方法,那么类中必须同样拥有,否则将会报错。

通过implements进行约束

你也可以在接口中描述一个方法,在类里实现它,如同下面的setTime方法一样:

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 Girl {
    say():string
}

interface Teacher extends Girl {
    name:string
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值