TS入门篇 | 详解 TypeScript 接口类型(上)

简介: 在JavaScript中,我们似乎很少听说接口这个概念,这是TypeScript中很常用的一个特性,它让 TypeScript 具备了 JavaScript 所缺少的、描述较为复杂数据结构的能力。下面就来看看什么是接口类型。

在JavaScript中,我们似乎很少听说接口这个概念,这是TypeScript中很常用的一个特性,它让 TypeScript 具备了 JavaScript 所缺少的、描述较为复杂数据结构的能力。下面就来看看什么是接口类型。


一、接口定义


接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法。


TypeScript 的核心原则之一是对值所具有的结构进行类型检查,并且只要两个对象的结构一致,属性和方法的类型一致,则它们的类型就是一致的。 在TypeScript里,接口的作用就是为这些类型命名和为代码或第三方代码定义契约。


TypeScript 接口定义形式如下:

interface interface_name { }
复制代码


来看例子,函数的参数是一个对象,它包含两个字段:firstName 和 lastName,返回一个拼接后的完整名字:

const getFullName = ({ firstName, lastName }) => {
  return `${firstName} ${lastName}`;
};
复制代码


调用时传入参数:

getFullName({
  firstName: "Hello",
  lastName: "TypeScript"
}); 
复制代码


这样调用是没有问题的,但是如果传入的参数不是想要的参数格式时,就会出现一些错误:

getFullName(); // Uncaught TypeError: Cannot destructure property `a` of 'undefined' or 'null'.
getFullName({ age: 18, phone: 110 }); // 'undefined undefined'
getFullName({ firstName: "Hello" }); // 'Hello undefined'
复制代码


这些都是我们不想要的,在开发时难免会传入错误的参数,所以 TypeScript 能够在编译阶段就检测到这些错误。下面来完善下这个函数的定义:

const getFullName = ({
  firstName,
  lastName,
}: {
  firstName: string; // 指定属性名为firstName和lastName的字段的属性值必须为string类型
  lastName: string;
}) => {
  return `${firstName} ${lastName}`;
};
复制代码


通过对象字面量的形式去限定传入的这个对象的结构,现在再来看下之前的调用会出现什么提示:

getFullName(); // 应有1个参数,但获得0个
getFullName({ age: 18, phone: 110 }); // 类型“{ age: number; phone: number; }”的参数不能赋给类型“{ firstName: string; lastName: string; }”的参数。
getFullName({ firstName: "Hello" }); // 缺少必要属性lastName
复制代码


这些都是在编写代码时 TypeScript 提示的错误信息,这样就避免了在使用函数的时候传入不正确的参数。我们可以使用interface来定义接口:

interface Info {
  firstName: string;
  lastName: string;
}
const getFullName = ({ firstName, lastName }: Info) =>
  return `${firstName} ${lastName}`;
};
复制代码


注意:在定义接口时,不要把它理解为是在定义一个对象,{}括号包裹的是一个代码块,里面是声明语句,只不过声明的不是变量的值而是类型。声明也不用等号赋值,而是冒号指定类型。每条声明之前用换行分隔即可,也可以使用分号或者逗号。


二、接口属性


1. 可选属性


在定义一些结构时,一些结构的某些字段的要求是可选的,有这个字段就做处理,没有就忽略,所以针对这种情况,TypeScript提供了可选属性。

定义一个函数:

const getVegetables = ({ color, type }) => {
  return `A ${color ? color + " " : ""}${type}`;
};
复制代码


这个函数中根据传入对象中的 color 和 type 来进行描述返回一句话,color 是可选的,所以可以给接口设置可选属性,在属性名后面加个?即可:

interface Vegetables {
  color?: string;
  type: string;
}
const getVegetables = ({ color, type }: Vegetables) => {
  return `A ${color ? color + " " : ""}${type}`;
};
复制代码


这里可能会报一个警告:接口应该以大写的i开头,可以在 tslint.json 的 rules 里添加"interface-name": [true, “never-prefix”]来关闭这条规则。

当属性被标注为可选后,它的类型就变成了显式指定的类型与 undefined 类型组成的联合类型,比如 getVegetables 方法的参数中的 color 属性类型就变成了这样:

string | undefined;
复制代码


那下面的接口与上述接口是一样的吗?

interface Vegetables2 {
  color?: string | undefined;
  type: string;
}
复制代码


答案肯定是否定的,因为可选意味着可以不设置属性键名,类型是 undefined 意味着属性键名不可选。


2. 只读属性


接口可以设置只读属性,如下:

interface Role {
  readonly 0: string;
  readonly 1: string;
}
复制代码


这里定义了一个角色,有 0 和 1 两种角色 id。下面定义一个角色数据,并修改一下它的值:

const role: Role = {
  0: "super_admin",
  1: "admin"
};
role[1] = "super_admin"; // Cannot assign to '0' because it is a read-only property
复制代码


这里TypeScript 提示不能分配给索引0,因为它是只读属性。

在ES6中,使用const定义的常量定义之后不能再修改,这和只读意思接近。那readonlyconst在使用时该如何选择呢?那主要看这个值的用途,如果是定义一个常量,那用const,如果这个值是作为对象的属性,就用readonly

const NAME: string = "TypeScript";
NAME = "Haha"; // Uncaught TypeError: Assignment to constant variable
const obj = {
  name: "TypeScript"
};
obj.name = "Haha";
interface Info {
  readonly name: string;
}
const info: Info = {
  name: "TypeScript"
};
info["name"] = "Haha"; // Cannot assign to 'name' because it is a read-only property
复制代码


上面使用const定义的常量NAME定义之后再修改会报错,但是如果使用const定义一个对象,然后修改对象里属性的值是不会报错的。所以如果要保证对象的属性值不可修改,需要使用readonly

需要注意,readonly只是静态类型检测层面的只读,实际上并不能阻止对对象的修改。因为在转译为 JavaScript 之后,readonly 修饰符会被抹除。因此,任何时候与其直接修改一个对象,不如返回一个新的对象,这是一种比较安全的方式。


3. 多余属性检查


先来看下面的例子:

interface Vegetables {
  color?: string;
  type: string;
}
const getVegetables = ({ color, type }: Vegetables) => {
  return `A ${color ? color + " " : ""}${type}`;
};
getVegetables({
  type: "tomato",
  size: "big"     // 'size'不在类型'Vegetables'中
});
复制代码


这里没有传入 color 属性,因为它是一个可选属性,所以没有报错。但是多传入了一个 size 属性,这时就会报错,TypeScript就会提示接口上不存在这个多余的属性,所以只要是接口上没有定义这个属性,在调用时出现了,就会报错。


注意: 这里可能会报一个警告:属性名没有按开头字母顺序排列属性列表,可以在 tslint.json 的 rules 里添加"object-literal-sort-keys": [false]来关闭这条规则。


有时 不希望TypeScript这么严格的对数据进行检查,比如上面的函数,只需要保证传入getVegetables的对象有type属性就可以了,至于实际使用的时候传入对象有没有多余的属性,多余属性的属性值是什么类型,我们就不管了,那就需要绕开多余属性检查,有如下方式:


(1) 使用类型断言

类型断言就是告诉 TypeScript,已经自行进行了检查,确保这个类型没有问题,希望 TypeScript 对此不进行检查,这是最简单的实现方式,类型断言使用 as 关键字来定义(这里不细说,后面进阶篇会专门介绍类型断言):

interface Vegetables {
  color?: string;
  type: string;
}
const getVegetables = ({ color, type }: Vegetables) => {
  return `A ${color ? color + " " : ""}${type}`;
};
getVegetables({
  type: "tomato",
  size: 12,
  price: 1.2
} as Vegetables);
复制代码


(2) 添加索引签名

更好的方式是添加字符串索引签名:

interface Vegetables {
  color: string;
  type: string;
  [prop: string]: any;
}
const getVegetables = ({ color, type }: Vegetables) => {
  return `A ${color ? color + " " : ""}${type}`;
};
getVegetables({
  color: "red",
  type: "tomato",
  size: 12,
  price: 1.2
});
复制代码




相关文章
|
17天前
|
JavaScript 安全 前端开发
TypeScript类型声明:基础与进阶
通过本文的介绍,我们详细探讨了TypeScript的基础与进阶类型声明。从基本数据类型到复杂的泛型和高级类型,TypeScript提供了丰富的工具来确保代码的类型安全和可维护性。掌握这些类型声明能够帮助开发者编写更加健壮和高效的代码,提高开发效率和代码质量。希望本文能为您在使用TypeScript时提供实用的参考和指导。
30 2
|
1月前
|
JavaScript 开发者
在 Babel 插件中使用 TypeScript 类型
【10月更文挑战第23天】可以在 Babel 插件中更有效地使用 TypeScript 类型,提高插件的开发效率和质量,减少潜在的类型错误。同时,也有助于提升代码的可理解性和可维护性,使插件的功能更易于扩展和升级。
|
2月前
|
JavaScript 前端开发
TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第11天】TypeScript【类型别名、泛型】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
2月前
|
JavaScript 前端开发 Java
TypeScript【接口】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第10天】TypeScript【接口】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
2月前
|
JavaScript 前端开发 安全
TypeScript【基础类型】超简洁教程!再也不用看臭又长的TypeScript文档了!
【10月更文挑战第9天】TypeScript【基础类型】超简洁教程!再也不用看臭又长的TypeScript文档了!
|
1月前
|
JavaScript 前端开发 安全
TypeScript进阶:类型系统与高级类型的应用
【10月更文挑战第25天】TypeScript作为JavaScript的超集,其类型系统是其核心特性之一。本文通过代码示例介绍了TypeScript的基本数据类型、联合类型、交叉类型、泛型和条件类型等高级类型的应用。这些特性不仅提高了代码的可读性和可维护性,还帮助开发者构建更健壮的应用程序。
27 0
|
2月前
|
JavaScript 索引
TypeScript(TS)安装指南与基础教程学习全攻略(二)
TypeScript(TS)安装指南与基础教程学习全攻略(二)
56 0
|
2月前
|
JavaScript 前端开发 安全
TypeScript(TS)安装指南与基础教程学习全攻略(一)
TypeScript(TS)安装指南与基础教程学习全攻略(一)
29 0
|
3月前
|
JavaScript
typeScript进阶(9)_type类型别名
本文介绍了TypeScript中类型别名的概念和用法。类型别名使用`type`关键字定义,可以为现有类型起一个新的名字,使代码更加清晰易懂。文章通过具体示例展示了如何定义类型别名以及如何在函数中使用类型别名。
44 1
typeScript进阶(9)_type类型别名
|
2月前
|
JavaScript 前端开发 安全
深入理解TypeScript:增强JavaScript的类型安全性
【10月更文挑战第8天】深入理解TypeScript:增强JavaScript的类型安全性
52 0