最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

TypeScript 入门自学笔记 — 接口的使用(六)

来源:博客园
目录
  • 一.函数接口参数
  • 二.函数类型接口
  • 三.函数混合类型
  • 四.对象接口(最常用)
    • 确定属性
    • 可选属性
    • 任意属性
    • 只读属性
  • 可索引接口
  • 索引访问符
  • 类接口
  • 接口继承
  • 构造函数类型
  • type 和 interface 的区别
在 TS 中,使用接口(Interfaces)来定义对象的类型。可用于对类的一部分行为进行抽象以外,也常用于对对象的形状(Shape)进行描述。(接口都是抽象的,接口中不能含有具体的实现逻辑)

一.函数接口参数

const fullName = ({ firstName, lastName }: { firstName: string; lastName: string }): string => {  return firstName + lastName;};// 可以约束函数中的参数,但是类型无法复用,可以通过接口进行描述interface IFullName {  firstName: string;  lastName: string;}const fullName = ({ firstName, lastName }: IFullName): string => {  return firstName + lastName;};getFullName({ firstname: "j", lastname: "w" });

二.函数类型接口

通过接口限制函数的参数类型和返回值类型

interface IFullName {  firstName: string;  lastName: string;}interface IFn {  (obj: IFullName): string;}const fullName: IFn = ({ firstName, lastName }) => {  return firstName + lastName;};

三.函数混合类型

interface ICounter {  (): number; // 限制函数类型  count: 0; // 限制函数上的属性}let fn: any = () => {  return fn.count++;};fn.count = 0;let counter: ICounter = fn;console.log(counter());console.log(counter());

四.对象接口(最常用)

对象接口用来描述对象的形状结构,定义一个接口 Person(接口一般首字母大写),定义一个变量 tom 类型是 Person。这样就约束了 tom 的形状必须和接口 Person 一致。


(资料图片仅供参考)

interface Person {  name: string;  age: number;}let p1: Person = {  name: "Tom",  age: 25,};

确定属性

确定属性:赋值时,定义的变量的形状必须与接口形状保持一致。变量的属性比接口少或多属性都是不允许的:

interface Person {  name: string;  age: number;}let p1: Person = {  // 类型中缺少属性 "age",但类型 "Person" 中需要该属性。  name: "Tom",};let p2: Person = {  // 多余 gender 属性  name: "Tom",  age: 25,  gender: "male",};

可选属性

可选属性:是指该属性可以不存在。有时不需要完全匹配一个接口时,可以用可选属性,但此时仍然不允许添加未定义的属性

interface Person {  name: string;  age?: number;}let p1: Person = {  // 编译通过  name: "Tom",};let p2: Person = {  // 编译通过  name: "Tom",  age: 25,};let p3: Person = {  // 多余 gender 属性  name: "Tom",  age: 25,  gender: "male",};

其他三种不太常用的 实现可选参数的方法:

  1. 断言案例:
interface IVeg {  color: string;  taste: string;  size: number;}let veg: IVeg = {  color: "red",  taste: "sour",  size: 10,  xx: 1,} as IVeg;
  1. 同名接口案例:
interface IVeg {  color: string;  taste: string;  size: number;}interface IVeg {  xx: number;}let veg: IVeg = {  color: "red",  taste: "sour",  size: 10,  xx: 1,};
  1. 基于当前类型,声明一个新的类型,实现参数的可选
interface IVeg {  color: string;  taste: string;  size: number;}interface IVegWithX extends IVeg {  xx?: number;}let veg: IVegWithX = {  // 此时变量的类型 为新创建的类型  color: "red",  taste: "sour",  size: 10,  xx: 1,};

任意属性

任意属性:允许一个接口有任意的属性,使用 [propName: string] 定义了任意属性的属性名取 string 类型的值。属性值为任意值 any,任意属性对必填属性做限制,其余的可以随意增减

interface Person {  name: string;  age?: number;  // 对象的key 可以有三种类型 string number symbol  [propName: string]: any;}let p1: Person = {  name: "Tom",  gender: "male",  [Symbol()]: "xxx",};

注意:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

例一:任意属性的类型是 string,但是可选属性 age 的值却是 number,number 不是 string 的子属性,所以会报错。

interface Person {  name: string;  age?: number;  [propName: string]: string;}let p1: Person = {  // 属性“age”与索引签名不兼容。不能将类型“number”分配给类型“string”。  name: "Tom",  age: 25,  gender: "male",};

例二:一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,可以在任意属性中使用联合类型:

interface Person {  name: string;  age?: number;  [propName: string]: string | number;}let p1: Person = {  // 编译通过  name: "Tom",  age: 25,  gender: "male",  year: 2021,};

只读属性

对象中的一些字段只能在创建时被赋值,可以使用 **readonly **定义只读属性:

例一:使用 readonly 定义的属性 id 初始化后,又被重新赋值,所以会报错。

interface Person {  readonly id: number;  name: string;  age?: number;  [propName: string]: any;}let p1: Person = {  id: 89757,  name: "Tom",  gender: "male",};p1.id = 9527; // 无法分配到 "id" ,因为它是只读属性。

例二:只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值时:

interface Person {  readonly id: number;  name: string;  age?: number;  [propName: string]: any;}let p2: Person = {  // 缺少属性 "id",但类型 "Person" 中需要该属性。  // 第一次给对象赋值  name: "Tom",  gender: "male",};p2.id = 89757;

可索引接口

可索引接口可以用于标识数字索引、数组

interface IArr {  [key: number]: any;}const obj: IArr = {  0: 100,  "1": 10, // 数字字符串可以转为数字,所以编译会通过  2: 200,};let arr: IArr = [1, "d", "c"];

索引访问符

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

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

类接口

描述类中的属性和方法。一个类可以实现多个接口,在类中必须实现接口中的方法和属性

interface Speakable {  name: string;  speak(): void;}interface SpeakChinese {  speakChinese(): void;}class Speak implements Speakable, SpeakChinese {  name!: string;  speak() {}  speakChinese() {}}

接口继承

interface Speakable {  name: string;  speak(): void;}interface SpeakChinese {  speakChinese(): void;}interface SpeakEnglish extends Speakable, SpeakChinese {  speakEnglish(): void;}// 可以实现多个类型class Speak implements SpeakEnglish {  name!: string;  speak(): void {    console.log("speak");  }  speakChinese(): void {    console.log("speakChinese");  }  speakEnglish(): void {    console.log("speakEnglish");  }}

构造函数类型

interface Clazz {  new (): any;}function createClass(target: Clazz) {  return new target(); // 传入的是一个构造函数}class Animal {}let r = createClass(Animal);

但是上述代码无法标识返回值类型,此时需要引入泛型的概念,在下一章节会详细学习泛型的概念及使用方法,

new() 表示当前是一个构造函数类型。 在使用 createClass 时动态传入类型。

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。泛型类似于函数的参数 , 泛型的声明一般采用一个大写字母来表示,其中 T 代表 Type,在定义泛型时通常用作第一个类型变量名称。但实际上 T 可以用任何有效名称代替

interface Clazz {  new (): T;}function createClass(target: Clazz): T {  return new target();}class Animal {}let r = createClass(Animal);

type 和 interface 的区别

interface通常描述 对象、类的结构比较多,可以被扩展和实现、继承、混合类型,能重名type描述函数的签名、联合类型、工具类型、映射条件类型,不能重名

关键词: 只读属性 构造函数 对象接口