TypeScript - Interfaces(接口)

news2025/1/19 8:11:55

目录

1、接口介绍

1.1 接口示例

2、可选属性

3、只读属性

4、额外的属性检查

5、函数类型

6、可索引的类型

7、类类型

7.1 类静态部分和实例部分

8、继承接口

9、混合类型

10、接口继承类


1、接口介绍

TypeScript 的核心原则之一是对值所具有的_结构_进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

1.1 接口示例

接口声明是命名对象类型的另一种方法,看下示例:

function people(obj: { name: string, age: number }) {
  console.log(obj.name+ '年龄是:'+obj.age);
}
let obj = { age: 20, name: '张三', addr: '北京市' }; 
people(obj)

// 张三年龄是:20

在这里people方法中,可以传入一个对象,对象必须一个name属性,和一个age属性,就是在传入传参数的时候,如果直接传递的是一个对象,那么这个对象一定是方法声明对象的父集,就是方法people中对象的属性,在传入对象中都能找到,且对应属性类型必须一致。

看另一种写法:

function people(obj: { name: string, age: number }) {
  console.log(obj.name+ '年龄是:'+obj.age);
}
people({age:20, name: 'z', addr: '北京市'})

// 对象字面量只能指定已知属性,并且“addr”不在类型“{ name: string; age: number; }”中。

当然,我们事先定义接口,或者类型别名也是可以的,如下所示:

interface MyInterface {
  name: string,
  age: number
}
type MyType = {
  name: any,
  age: number,
  addr: '北京市'
}

function people(obj: MyInterface | MyType) {
  console.log(obj.name+ '年龄是:'+obj.age);
}
let myObj = {age:20, name: '张三', addr: '北京市'}
people(myObj)

// 张三年龄是:20

只要MyInterface 和Mytype同时都是name和age属性就可以,因为检查器会逐个检查每个属性是否存在,以及它们对应的类型,类型必须小于等于定义的类型即可。在这里不像其他的面向对象语言,例如:Java,向他们那样必须去实现某个接口,它们这些是编译型语言,在编译的是否会有严格的限制,JavaScript属于解释性语言,限制没那么严格,而TypeScript是JavaScript的超集,自然也遵从这个规定,只要传入的接口或类型别名有对应的属性就行,而这也是TypeScript有时被称做“鸭式辨型法”的原因。

鸭式辨型来自于James Whitecomb Riley的名言:"像鸭子一样走路并且嘎嘎叫的就叫鸭子。

2、可选属性

接口里的属性不全都是必需的。 有些是只在某些条件下存在,或者根本不存在。 可选属性在应用“option bags”模式时很常用,即给函数传入的参数对象中只有部分属性赋值了。

下面是一个示例:

interface MyInterface {
  name: string,
  age: number
  addr?: string
}

function people(obj: MyInterface) {
  console.log(obj.name+ '年龄是:'+obj.age);
}
let myObj = {age: 20, name: '张三'}
people(myObj)

可选属性,就是在属性后面加了个问号(?)所以添加问号的属性,都是非必填属性。

可选属性的好处就是可以进行预定义,后续可以根据实际情况去判断该属性是否存在,当然这种方式还有一种好处,在写代码的时候可以自动进行智能提示,找到对应的属性。

如下所示:

 这样也可以防止我们写错属性而没有被发现。

3、只读属性

在声明对象属性的时候,可以在属性名之前添加 readonly 来指定是只读属性。

interface People = {
    readonly name: string,
    readonly age: number
}
let p1:People = {name: '张三', age: 12}
p1.age = 24; // 编译报错 无法为“age”赋值,因为它是只读属性。

 对象被重新构建之后,所有的属性都不能变更了。

ReadonlyArray<T>类型,可以保证数组创建之后也不能进行变更了,可读数组类型,实在ES2019(ES10)之后所添加,如下所示:

let numArr:number[] = [10, 30, 50, 70, 90];
let readonlyArr:ReadonlyArray<number> = numArr;
readonlyArr[0] = 123; // 类型“readonly number[]”中的索引签名仅允许读取。
readonlyArr.push(123) // 类型“readonly number[]”上不存在属性“push”
readonlyArr.splice(1); // 属性“splice”在类型“readonly number[]”上不存在
readonlyArr.length = 1  // 无法为“length”赋值,因为它是只读属性。
numArr = readonlyArr  // 不能分配给可变类型 "number[]"

4、额外的属性检查

在对象中指定了可选属性,如果传入的是一个对象字面量,而这个对象字面量中的属性,和之前对象定义的属性不一致时,会编译报错,说对象字面量只能指定已知的属性。

例如:那属性name写成names时,如下所示:

interface MyInterface {
    name?: string,
    age?: number
    addr?: string
  }
  
  function people(obj: MyInterface) {
    console.log(obj.name+ '年龄是:'+obj.age);
  }
  people({age: 20, names: '张三'})
 // 类型“{ age: number; names: string; }”的参数不能赋给类型“MyInterface”的参数。

如果跳过编译检查,有以下两种方式:

  // 第一种方式,使用类型断言
  people({age: 20, names: '张三'} as MyInterface)
  // 第二种方式,把字面量对象赋值给一个变量。
  let obj = {age: 20, names: '张三'}
  people(obj)

第二种方式,虽然能跳转编译检查,但可能会产生bug,虽然能运行,但是结果会与预期不一样,所以,最好能提前规避,在编译期间找出对应的bug.

如果我们确定对象可能会有多个未知的属性,我们可以添加字符串索引签名,类似于在JavaScript函数,传参一般(...obj),说明该函数接收的参数可能还有其他多个。

interface MyInterface {
    name?: string,
    age?: number,
    addr?: string,
    [other:string]: any
}

5、函数类型

我们可以使用接口表示函数类型,参数属性和之前对象属性定义一样,末尾再加上函数的返回类型,如下所示:

interface MyInterface {
    (name: string, age: number, addr?:string) :string
  }
  
let p1:MyInterface;  
p1 = function(name:string, age:number, addr?:string) {
    return name+ '年龄是:'+age
}
console.log('p1: ', p1("张三", 20));

对象函数参数定义和JavaScript中一样,名字不一定要和接口里定义的一样,函数中的参数,与传参顺序有关,所以在这里,实际传的值与函数参数顺序对应起来就可以了。

如果函数中没有指定参数类型,会根据接口定义进行自动推断。

interface MyInterface {
    (name: string, age: number, addr?:string) :string
  }
  
let p1:MyInterface;  
p1 = function(a, b, addr) {
    return a+ '年龄是:'+b
}
console.log('p1: ', p1("张三", 20));

6、可索引的类型

我们可以使用索引访问类型在另一种类型上查找特定属性:

type Person = { age: number; name: string; alive: boolean };
let p1: Person;
p1= {age: 10, name: '张三', alive: true}
console.log('Person["age"]: ', p1["age"]);
// Person["age"]:  10     

索引类型本身就是一个类型,因此我们可以完全使用联合、 或其他类型的类型:keyof

type Person = { age: number; name: string; alive: boolean };

// type I1 = string | number
type I1 = Person["age" | "name"];     

// type I2 = string | number | boolean
 
type I2 = Person[keyof Person];
     
type AliveOrName = "alive" | "name";
// type I3 = string | boolean
type I3 = Person[AliveOrName];

如果您尝试为不存在的属性编制索引,您甚至会看到错误:

type Person = { age: number; name: string; alive: boolean };

// 编译报错,类型“Person”上不存在属性“alve”。
type I1 = Person["alve"];

用于获取数组元素的类型,我们可以将其方便地捕获数组对应的元素类型:number typeof

const MyArray = [
    { name: "Alice", age: 15 },
    { name: "Bob", age: 23 },
    { name: "Eve", age: 38 },
  ];
   
// type Person = {
//     name: string;
//     age: number;
// }  
type Person = typeof MyArray[number];

// type Age = numbers
type Age = typeof MyArray[number]["age"];     
// Or
type Age2 = Person["age"];

只能在索引时使用类型,这意味着不能使用 进行变量引用:const

const key = "age";
//报错,类型“key”不能作为索引类型使用。
type Age = Person[key];
// 可以
type Age1 = Person['age'];

用类型别名是可以的。

type key = "age";
// 可以 type Age = number
type Age = Person[key];

7、类类型

C#或Java里接口的基本作用一样,TypeScript也能够用它来明确的强制一个类去符合某种契约。也就是面对对象编程中类的继承概念,定义一个接口,由类去实现这个接口。

interface animal {
    birthday: Date
    getInfo(): string
}

class Cat implements animal {
    birthday: Date;
    name: string;
    getInfo() :string {
        return this.name + ", " + this.birthday;
    }
    constructor(name: string, birthday: Date) {
        this.name = name;
        this.birthday = birthday;
    }
}

 let c1 = new Cat('叮当猫', new Date('2023-05-09'));
 console.log(c1.getInfo());
 // 叮当猫, Tue May 09 2023 08:00:00 GMT+0800 (China Standard Time)

7.1 类静态部分和实例部分

在操作类和接口的时候,当你用构造器签名去定义一个接口并试图定义一个类去实现这个接口时会得到一个错误:

interface Animal {
    birthday: Date
    new(name: string, birthday: Date) 
}
// 编译错误,类型“Cat”提供的内容与签名“new (name: string, birthday: Date): any”不匹配。
class Cat implements Animal {
    birthday: Date;
    name: string;
    getInfo() :string {
        return this.name + ", " + this.birthday;
    }
    constructor(name: string, birthday: Date) {
        this.name = name;
        this.birthday = birthday;
    }
}

所以,在类的静态部分我们做一些改变,我们再增加一个接口AnimalInter,然后把原接口构造器添加一个返回值为AnimalInter接口,新建一个函数,函数的返回类型为AnimalInter接口,而函数的第一个参数进行实例化,如下图所示:

interface Animal {
    new(name: string, birthday: Date): AnimalInter 
}

interface AnimalInter {
    getInfo() : string
}

function CreateFunc(first:Animal, name: string, birthday: Date ):AnimalInter {
    return new first(name, birthday)
}

class Cat{ 
    birthday: Date;
    name: string;
    getInfo() {
        return this.name + ", " + this.birthday;
    }
    constructor(name: string, birthday: Date) {
        this.name = name;
        this.birthday = birthday;
    }
}

let result = CreateFunc(Cat, '张三', new Date('2023-02-09'))
console.log('result: ', result.getInfo());

这样在示例化这个Cat类的时候,检查是否和Animal接口构造函数定义的一致。

8、继承接口

接口的继承和类是一样的,是可以相互继承的,这样可以进行拆分多个接口,便于以后的可复用,重构起来也更方便,如下所示:

interface Animal {
    name: string,
}

interface AnimalInter extends Animal {
    getInfo() : string
}

class Cat implements AnimalInter{ 
    birthday: Date;
    name: string;
    getInfo() {
        return this.name + ", " + this.birthday;
    }
    constructor(name: string, birthday: Date) {
        this.name = name;
        this.birthday = birthday;
    }
}

let result = new Cat('张三', new Date('2023-02-09'))
console.log('result: ', result.getInfo());

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

interface Animal {
    name: string,
}
interface AnimalSecond {
    age: number,
}

interface AnimalInter extends Animal, AnimalSecond {
    getInfo() : string
}

class Cat implements AnimalInter{ 
    birthday: Date;
    name: string;
    age: number;
    getInfo() {
        return this.name + ", " + this.birthday + ", age: "+this.age;
    }
    constructor(name: string, birthday: Date, age: number) {
        this.name = name;
        this.birthday = birthday;
        this.age = age;
    }
}

let result = new Cat('张三', new Date('2023-02-09'), 20)
console.log('result: ', result.getInfo());

9、混合类型

混合类型,一个变量它的类型可能有多个,number | string| boolean 等,一个对象也可能是,以下就是一个对象,还可以是一个函数。

interface Animal {
    (age: number): string;
    name: string;
    time: number;
}

function getAnimal (): Animal {
    let animal = function(age: number) {} as Animal
    return animal;
}
let getResult = getAnimal();
getResult(20);
getResult.time = 20;
// 报错,无法为“name”赋值,因为它是只读属性。函数的name,是可读的
getResult.name = '张三';
console.log(getResult.time);
console.log(getResult.name ); 

10、接口继承类

接口继承类时,会继承类的成员,不包括对应的实现。接口可以继承类的私有成员,和受保护的成员,如果需要继承这个接口,只能由接口继承类的子类继承。在构造器函数中,子类的构造函数必须包含 "super" 的调用。

class AnimalClass {
   private name: any;
}

interface AnimalInterface extends AnimalClass{
    getName(): string; 
}

class Animal extends AnimalClass implements AnimalInterface{
    getName() {
        return '123';
    };
    constructor() {
       super();
    }
}

// 类型 "Cat" 中缺少属性 "name",但类型 "AnimalInterface" 中需要该属性。
class Cat implements AnimalInterface {
    getName() {
        return ''
    }
}

let anima = new Animal()

Animal类继承AnimalClass类,在构造器中,必须调用super() 方法,子类进行初始化的时候,父类中有定义成员属性也需进行初始化操作,而AnimalInterface接口只能被AnimalClass类的子类所实现,因为本身接口就继承了AnimalClass类,而Cat在实现AnimalInterface接口时,必须包含接口从其他类中继承的所有属性,包括私有属性,Cat类不是AnimalInterface接口继承类的子类,所以就报错了,而父类私有属性不能通过子类实例化后的对象进行直接访问,也不会被继承。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/556404.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索未来:物联网的无限可能

连接万物&#xff0c;创造未来。从智能家居到智慧医疗&#xff0c;从智能车联到智慧城市&#xff0c;物联网技术的影响已经悄然渗透到了我们的方方面面。欢迎大家积极讨论联网技术如何影响了我们的生活。 物联网技术概述 物联网技术&#xff08;Internet of Things&#xff0…

WebStorm 固定 调试版 Chrome

WebStorm 固定 调试版 Chrome 每次升级 WebStorm 都会打开一个新的 Chrome&#xff0c;导致调试时需要重新登录&#xff0c;重新安装插件等问题。 解决办法&#xff1a; 固定 WebStorm 中 Chrome UserData 的路径&#xff0c;这样每次打开的 Chrome 都是同一个。 文件 | 设…

HJ73 计算日期到天数转换

1.题目&#xff1a; 2.分析&#xff1a; 1. 通过枚举每个月的1号是这一年的第几天&#xff0c;从而进行累加求和即可&#xff0c;其中注意闰年的处理 3.我的代码&#xff1a; #include <iostream> using namespace std;int main() {int arr[13] { 0, 31, 28, 31, 30,…

设计模式基础-面向对象基础

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 这里只是简单的将《大话设计模式【Java溢彩加强版】》的内容简单是复述一下&#xff0c;并加上自己的理解 在学习Java设计模式的时候…

Hadoop集群实现时间同步

一.为什么要对集群实现时间同步 因为我们在集群使用hive&#xff0c;mysql&#xff0c;hdfs之间等使用sqoop传输数据的时候&#xff0c;如果集群之间没有同步时间的话&#xff0c;那么就会报错&#xff0c;无法实现数据的传输。 不仅如此&#xff0c;在集群的使用当中&#xff…

logstash的快速使用

同品&#xff1a; filebeat&#xff1a;只做数据收集&#xff0c;讲数据输出到指定目的地&#xff0c;占用资源小 logstash:收集日志数据&#xff0c;还能过滤&#xff0c;转换数据&#xff0c;组需要更多资源 目录 1.logstash的安装 2.配置文件 3.创建容器 4.引入依赖 …

设计模式期末程序题(只是一个简单整理)

1.下图是某系统的数据部分的类图。因为该层次结构中的操作需要经常变化&#xff0c;所以需要用访问者模式对其进行重构&#xff0c;请按以下要求完成题目&#xff1a; &#xff08;1&#xff09;绘制重构后系统完整类图。&#xff08;4分&#xff09; &#xff08;2&#xff09…

第一章 数学基础

目录 一、线性代数二、微积分三、概率 一、线性代数 理解范数概念区分向量的内积 a ⋅ b \mathbf{a} \cdot \mathbf{b} a⋅b 与外积 a b \mathbf{a} \times \mathbf{b} ab区分矩阵的乘法 A ⊗ B \mathbf{A} \otimes \mathbf{B} A⊗B、内积 A B \mathbf{A} \mathbf{B} AB 、…

通过Python的PyPDF2库提取pdf中的图片

文章目录 前言一、PyPDF2库是什么&#xff1f;二、安装PyPDF2库三、查看PyPDF2库版本四、使用方法待提取的pdf截图1.引入库2.定义pdf路径3.打开PDF文件4.创建PDF阅读器对象5.获取PDF文件中的页数6.遍历每一页进行处理7.提取出来的图片 总结 前言 大家好&#xff0c;我是空空sta…

【Linux初阶】进程程序替换 | 初识、原理、函数、应用 makefile工具的多文件编译

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;替换初识&#xff0c;替换原理&#xff0c;替换函数理解和使用&#xff0c;makefile工具的多文件编译&#xf…

C++(4):表达式

表达式由一个或多个运算对象(operand)组成,对表达式求值将得到一个结果(result&#xff09;。字面值和变量是最简单的表达式&#xff08;expression)&#xff0c;其结果就是字面值和变量的值。把一个运算符&#xff08;operator)和一个或多个运算对象组合起来可以生成较复杂的表…

倒挂的解决方案你现在是一位计算机专家,来聊一聊:“美国的火星探测器Mars Path-finder 就是因为优先级倒挂而出现故障的故事”

目录 倒挂的解决方案 你现在是一位计算机专家&#xff0c;来聊一聊&#xff1a;“美国的火星探测器Mars Path-finder 就是因为优先级倒挂而出现故障的故事” ●使用中断禁止 具体证明请参阅Liu和Kayland于1973年发表的论文。 ● 因时序或外部中断或进程挂起而导致操作系统获…

数据结构-关键路径-理论

1.AOE-网 与AOV-网相对应的是AOE-网&#xff08;Activity On Netword&#xff09;&#xff0c;即以边表示活动的网。AOE-网是带权的有向无环图&#xff0c;其中&#xff0c;定点表示时间&#xff0c;弧表示活动&#xff0c;权表示活动持续的时间。通常AOE-网可用来估算工程的完…

Base64字符串从前台传到后台以后,“+”加号消失

记录一下问题&#xff1a; 使用 encodeURI(str) 对字符串进行加密的时候&#xff0c;后端解密会丢失 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content&…

第一章.机器学习的基础概念

第一章.机器学习的基础概念 1.1 机器学习的基础概念 1.机器学的概念&#xff1a; 机器学习就是机器从数据中总结经验。从数据中找出某种规律或者模型&#xff0c;并用他来解决某种实际问题。 2.机器学习的应用场景 1).事物本身存在某种潜在规律 2).某些问题难以使用普通编程…

two-stage目标检测算法

R-CNN 现在&#xff0c;将目光穿越回2012年&#xff0c;hinton刚刚提出alexnet的时代。 此时&#xff0c;该如何审视目标检测任务&#xff1f; 当时的目标检测采用的是滑动窗口手动特征分类器的思路。 该方法的弱点包括 速度慢 精度差 精度差的问题是由手工特征造成的&am…

【VS安装记录】Visual Studio 2022安装教程(超详细)

大家好&#xff0c;我是雷工&#xff01; 由于更换了电脑&#xff0c;很多软件需要重新安装&#xff0c;为了方便学习C#&#xff0c;今天有时间安装下Visual Studio 2022&#xff0c;顺便记录安装过程。 1、从官网下载并解压软件压缩包&#xff0c;然后打开文件夹。 2、双击…

切比雪夫不等式,大数定律及极限定理。

一.切比雪夫不等式 1.定理 若随机变量X的期望EX和方差DX存在,则对任意ε > 0,有   P{ |X - EX| > ε } < DX/ε2 或 P{ |X - EX| < ε } > 1 - DX/ε2 2.解析定理 ①该定理对 X 服从什么分布不做要求&#xff0c;仅EX DX存在即可。 ②“| |” 由于X某次…

linux kernel pwn 基础知识

基础知识 内核概述 内核架构 通常来说我们可以把内核架构分为两种&#xff1a;宏内核和微内核&#xff0c;现在还有一种内核是混合了宏内核与微内核的特性&#xff0c;称为混合内核。 宏内核&#xff08;Monolithic kernel&#xff09;&#xff0c;也译为集成式内核、单体…

网络原理——基础概念(端口号、分层、封装和复用)、各层协议(TCP/IP协议)(详细图解)

目录 一、基础概念 1、 IP地址 &#xff08;1&#xff09;点分十进制 2、端口号 3、协议 &#xff08;1&#xff09;协议的 组成部分 &#xff08;2&#xff09; 协议的 作用 4、五元组 5、协议分层 &#xff08;1&#xff09;分层的 好处 &#xff08;2&#xff0…