高德地图打字界面
There are many reasons to like TypesScript because of the many advantages it has over JavaScript. The primary benefit for most developers is the advantage of type safety. Type safety is accomplished a number of ways within TypeScript, but one of the most important is through interfaces.
有很多理由喜欢TypesScript,因为它比JavaScript具有许多优势。 对于大多数开发人员而言,主要好处是类型安全性的优点。 在TypeScript中,可以通过多种方法来实现类型安全,但是最重要的方法之一是通过接口。
Interfaces allow you to verify that your functions have all the data they need and nothing that they do not need. TypeScript will tell you this all before your code even is compiled.
接口使您可以验证函数是否具有所需的所有数据,而不需要不需要的数据。 TypeScript会在编译代码之前告诉您所有这些信息。
总览 (Overview)
Here is an overview of what this article will cover.
这是本文将介绍的内容的概述。
- What are TypeScript interfaces? 什么是TypeScript接口?
- When do you use TypeScript interfaces? 什么时候使用TypeScript接口?
- Simple interface example 简单的界面示例
- Adding properties to an interface向接口添加属性
- Removing properties from an interface, and optional properties从接口删除属性和可选属性
- Handling multiple JSON objects处理多个JSON对象
- Handling an array of multiple JSON objects处理多个JSON对象的数组
- Using function properties使用函数属性
- Conclusion and additional resources结论和其他资源
什么是TypeScript接口?(What Are TypeScript Interfaces?)
One of the common explanations of TypeScript interfaces is that they define a contract, but what does that mean? It basically means that an interface will specify what properties and what those properties’ type (or types) should be.
TypeScript接口的常见解释之一是它们定义了合同,但这意味着什么? 基本上,这意味着接口将指定什么属性以及那些属性的类型应该是什么。
It’s good to have a small explanation of what interfaces are, but the best way to understand interfaces is through examples and actually using them.
最好对接口是什么进行一些解释,但是了解接口的最佳方法是通过示例并实际使用它们。
什么时候使用TypeScript接口? (When Do You Use TypeScript Interfaces?)
There are a few different ways to use interfaces, but the main two you will see are by using an interface as a parameter to a function and by determining the data structure from something like an API response.
有几种使用接口的方法,但是您将看到的主要两种方法是将接口用作函数的参数,并通过API响应之类的方法确定数据结构。
For this article, we will primarily talk about using an interface as a part of a parameter of a function. The vast majority of times you use an interface, it will be for this use case.
在本文中,我们将主要讨论使用接口作为函数参数的一部分。 您使用接口的绝大多数时间都是针对这种用例。
Later in this article, we will work with JSONPlaceholder and the user endpoint. Here is an example of using our User
interface as part of an API response. This a small example from an Angular service.
在本文的后面,我们将使用JSONPlaceholder和用户端点。 这是将我们的User
界面用作API响应的一部分的示例。 这是一个来自Angular服务的小例子。
getUsers() {
return this.http.get<User[]>(`https://jsonplaceholder.typicode.com/users`);
}
The above code basically just says to expect the data from that endpoint to conform to the properties and types that were specified in the User
interface.
上面的代码基本上只是说要期望来自该端点的数据符合User
界面中指定的属性和类型。
简单界面示例 (Simple Interface Example)
In our simple example, we will use just four fields: id, name, username, and email. This interface states that all four fields are required and that the id
must be of type number
, while the other three must be of type string
.
在简单的示例中,我们将仅使用四个字段:id,名称,用户名和电子邮件。 该接口说明所有四个字段都是必填字段,并且id
必须为number
类型,而其他三个字段必须为string
类型。
export interface User {
id: number;
name: string;
username: string;
email: string;
}
Now let’s use this interface in a function.
现在让我们在函数中使用此接口。
const getUser = (user: User) => {
console.log(`This user's name is: ${user.name}`);
};getUser({
id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere@april.biz',
});//This user's name is: Leanne Graham
Great! That all worked and was easy.
大! 一切正常,很容易。
向接口添加属性 (Adding Properties to an Interface)
What would happen if you tried to add an additional property not specified in the interface?
如果您尝试添加接口中未指定的其他属性,将会发生什么?

As you see above, the first thing that we did was try to add an additionalProperty
field to the object passed to getUser
. We get the error below.
如上所示,我们所做的第一件事是尝试向传递给getUser
的对象添加一个additionalProperty
的属性字段。 我们得到下面的错误。
Argument of type '{ id: number; name: string; username: string; email: string; additionalProperty: string; }' is not assignable to parameter of type 'User'.
Object literal may only specify known properties, and 'additionalProperty' does not exist in type 'User'.ts(2345)
This error is just stating that you have added a property, conveniently named additionalProperty
, that is not specified in the User
interface.
该错误仅表示您已添加一个属性,方便地命名为additionalProperty
,该属性未在User
界面中指定。
How do you fix this?
您如何解决这个问题?
You thankfully have two options to resolve this issue.
庆幸的是,您有两个选择可以解决此问题。
1. You could remove additionalProperty
from the object being passed into the getUser
function. Now that most likely isn’t going to be the solution you are looking for, as you likely intentionally made this change and need the information in the additionalProperty
. That said, it does happen that you add properties to an object unintentionally. For example, you may have intended to add the additionalProperty
as an additional parameter, not as a property, where the getUser
function potentially would look like the following:
1.您可以删除additionalProperty
从对象被传递到getUser
功能。 现在最有可能的是不会是你正在寻找的解决方案,因为你可能是刻意制造这种变化,需要在信息additionalProperty
。 就是说,您确实无意中向对象添加了属性。 例如,您可能打算将additionalProperty
作为附加参数添加, 而不是作为属性, getUser
函数的外观可能如下所示:
getUser(
{
id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere@april.biz',
},
{ additionalProperty: 'Another one' }
);
2. The other solution would be to update the User
interface to account for the new additionalProperty
with the appropriate type. Usually, when this error occurs, this is the solution you are looking for.
2.另一种解决方案将是,以更新User
界面以考虑新additionalProperty
与适当的类型。 通常,发生此错误时,这就是您要寻找的解决方案。
从接口删除属性和可选属性 (Removing Properties From an Interface, and Optional Properties)
So what if we do not need the email
property any more?
那么,如果我们不再需要email
属性怎么办?

As you see, if you just remove the email
property from the object passed to getUser
, you will see the error below.
如您所见,如果您只是从传递给getUser
的对象中删除email
属性,则会看到以下错误。
Argument of type ‘{ id: number; name: string; username: string; }’ is not assignable to parameter of type ‘User’.
Property ‘email’ is missing in type ‘{ id: number; name: string; username: string; }’ but required in type ‘User’.ts(2345)interfaces.ts(5, 3): ‘email’ is declared here.
This error is basically saying that email
is required to be passed as part of the User
interface.
基本上,此错误表示需要email
作为User
界面的一部分传递。
How do you fix this?
您如何解决这个问题?
Again, you have two options to fix this.
同样,您有两个选择可以解决此问题。
You could remove
email
from theUser
interface. This solution might be fine, but you will need to be certain that theemail
property would never be added as a property on the object passed togetUser
.您可以从
User
界面中删除email
。 这个解决方案可能很好,但是您需要确定email
属性永远不会作为传递给getUser
的对象的属性添加。You may have noticed the other solution in the example above, which is to add a
?
afteremail
making the property optional. This is a super nice feature of interfaces, allowing the addition or removal of theemail
property. Of course, you will likely need to make changes within thegetUser
function to handle the scenario whereemail
is not passed.您可能已经注意到上面示例中的另一个解决方案,即添加一个
?
在email
后,将该属性设置为可选。 这是界面的超好功能,允许添加或删除email
属性。 当然,您可能需要在getUser
函数中进行更改以处理未传递email
的情况。
处理多个JSON对象 (Handling Multiple JSON Objects)
We’re ramping things up a bit with this example, but let’s take a look at JSONPlaceholder. This an excellent resource for many things in software development, and it will be helpful for explaining TypeScript interfaces as well.
我们通过此示例进行了一些改进,但让我们看一下JSONPlaceholder。 这是软件开发中许多事情的绝佳资源,并且对于解释TypeScript接口也将有所帮助。
We will refer to the users endpoint, and for now we will only look at the first user at this endpoint https://jsonplaceholder.typicode.com/users/1
. You can also view this in Chrome, where you should see the JSON structure below.
我们将参考用户端点,现在我们仅查看该端点的第一个用户https://jsonplaceholder.typicode.com/users/1
。 您还可以在Chrom e中查看此内容,在此处应看到下面的JSON结构。
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
}
How should we go about creating an interface for this? Well, we have a basic idea of how to do this, based on our simple example.
我们应该如何为此创建一个接口? 好吧,基于我们的简单示例,我们对如何执行此操作有基本的了解。
Let’s ignore the address
and company
objects for now. If we do that, we will have a User
interface that looks like the one below.
现在让我们忽略address
和company
对象。 如果这样做,我们将拥有一个类似于以下界面的User
界面。
export interface User {
id: number;
name: string;
username: string;
email: string;
phone: string;
website: string;
}
Now how do you accommodate the address
and company
objects? We will start by creating a new interface for both.
现在,您如何容纳address
和company
对象? 我们将从为两者创建一个新接口开始。
export interface Address {
street: string;
suite: string;
city: string;
zipcode: string;
geo: {
lat: string;
lng: string;
};
}export interface Company {
name: string;
catchPhrase: string;
bs: string;
}
It is generally considered a best practice to create a new interface for each object. You will notice that we neglected to do that for geo
object. The reason for this is that this information is likely to always be accompanied by the address information. Of course, if you wanted to, you could create a GeoCoordinates
interface and modify the Address
interface, like so:
通常,最佳做法是为每个对象创建一个新接口。 您会注意到我们忽略了对geo
对象执行此操作。 其原因是该信息很可能总是伴随着地址信息。 当然,如果需要,您可以创建一个GeoCoordinates
接口并修改Address
接口,如下所示:
interface Address {
street: string;
suite: string;
city: string;
zipcode: string;
geo: GeoCoordinates;
}interface GeoCoordinates {
lat: string;
lng: string;
}
We now have these two new Address
and Company
interfaces. How do we associate these to the User
interface?
现在,我们有了这两个新的Address
和Company
接口。 我们如何将它们与User
界面关联?
export interface User {
id: number;
name: string;
username: string;
email: string;
address: Address; // uses the new Address interface
phone: string;
website: string;
company: Company; // uses the new Company interface
}
We are at a point now that our User
interface is fully set up. Let’s take a look at our updated getUser
function.
现在我们已经完全设置了User
界面。 让我们看一下更新后的getUser
函数。
const logUserAddress = (userAddress: Address) => {
console.log(`The user's address is ${userAddress.street} ${userAddress.suite}`);
};const getUser = (user: User) => {
console.log(`The user's name is ${user.name}`);
console.log(`The user's phone number is ${user.phone}`);
console.log(`The user's zipcode is ${user.address.zipcode}`);
logUserAddress(user.address);
};getUser({
id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere@april.biz',
address: {
street: 'Kulas Light',
suite: 'Apt. 556',
city: 'Gwenborough',
zipcode: '92998-3874',
geo: {
lat: '-37.3159',
lng: '81.1496',
},
},
phone: '1-770-736-8031 x56442',
website: 'hildegard.org',
company: {
name: 'Romaguera-Crona',
catchPhrase: 'Multi-layered client-server neural-net',
bs: 'harness real-time e-markets',
},
});//The user's name is Leanne Graham
//The user's phone number is 1-770-736-8031 x56442
//The user's zipcode is 92998-3874
//The user's address is Kulas Light Apt. 556
As you see in the getUser
function, the object that is passed is exactly the same as what you saw from https://jsonplaceholder.typicode.com/users/1
.
如在getUser
函数中看到的,传递的对象与从https://jsonplaceholder.typicode.com/users/1
看到的对象完全相同。
One additional change that was made to the getUser
function is that there is another function, named logUserAddress
, that is getting called within it. This new function takes a parameter of the new Address
interface we created. The reason this is nice is that you can only pass the user’s address details with the full benefit of the Address
interface and the types definitions of it.
对getUser
函数进行的另一项logUserAddress
是在其中调用了另一个名为logUserAddress
函数。 这个新函数采用我们创建的新Address
接口的参数。 这样很好的原因是,您只能利用Address
接口及其类型定义的全部好处来传递用户的地址详细信息。
处理多个JSON对象的数组 (Handling an Array of Multiple JSON Objects)
So we have ramped things up with the full user object from JSONPlaceholder, but let’s pretend that it’s even a bit more complicated still by adding an array of objects.
因此,我们通过JSONPlaceholder中的完整用户对象来加强了工作,但是我们假设通过添加对象数组,它甚至更加复杂。
Let’s assume our user is rich and has multiple houses, with each house’s details stored in an object within the address
array. Your user object is going to look something like the object below.
假设我们的用户很丰富并且有多个房屋,每个房屋的详细信息都存储在address
数组中的一个对象中。 您的用户对象将看起来像下面的对象。
{
id: 1,
name: 'Leanne Graham',
username: 'Bret',
email: 'Sincere@april.biz',
address: [
{
street: 'Kulas Light',
suite: 'Apt. 556',
city: 'Gwenborough',
zipcode: '92998-3874',
geo: {
lat: '-37.3159',
lng: '81.1496',
},
},
{
street: 'Random Street',
suite: 'Apt. 321',
city: 'Attenborough',
zipcode: '93187-4259',
geo: {
lat: '-37.9911',
lng: '82.0137',
},
},
],
phone: '1-770-736-8031 x56442',
website: 'hildegard.org',
company: {
name: 'Romaguera-Crona',
catchPhrase: 'Multi-layered client-server neural-net',
bs: 'harness real-time e-markets',
},
}
The only changes here are that address
is an array and there is a new object with dummy data that matches all of the required properties that are defined in the Address
interface.
唯一的变化是address
是一个数组,并且存在一个带有伪数据的新对象,该对象与Address
接口中定义的所有必需属性相匹配。
{
street: 'Random Street',
suite: 'Apt. 321',
city: 'Attenborough',
zipcode: '93187-4259',
geo: {
lat: '-37.9911',
lng: '82.0137',
},
},
So now that we have defined what our more advanced user object is going to look like, what are the ramifications for our User
interface?
因此,既然我们已经定义了高级用户对象的外观,那么User
界面的后果是什么?
export interface User {
id: number;
name: string;
username: string;
email: string;
address: Address | Address[];
phone: string;
website: string;
company: Company;
}
The |
is a very important piece here called a union type in TypeScript. This is basically saying that the address
property can either be one single Address
object or an array with one or more Address
objects. In our case, we could get by without using a union type and strictly require the address
property to be an array of Address
objects. But this solutions allows flexibility and would still work with the prior user JSON object provided from JSONPlaceholder.
|
这是一个非常重要的部分,在TypeScript中称为联合类型。 这基本上是说address
属性可以是一个单个Address
对象,也可以是具有一个或多个Address
对象的数组。 在我们的例子中,我们可以不使用联合类型来实现,并且严格要求address
属性是Address
对象的数组。 但是此解决方案具有灵活性,并且仍可以与JSONPlaceholder提供的先前的用户JSON对象一起使用。
const logUserAddress = (userAddress: Address | Address[]) => {
if (Array.isArray(userAddress)) {
console.log(`The user has ${userAddress.length} addresses`);
for (const [index, address] of userAddress.entries()) {
console.log(`The user's address ${index + 1} is ${address.street} ${address.suite}`);
}
} else {
console.log(`The user's address is ${userAddress.street} ${userAddress.suite}`);
}
};const getUser = (user: User) => {
console.log(`The user's name is ${user.name}`);
console.log(`The user's phone number is ${user.phone}`);
logUserAddress(user.address);
};
After taking a look at the functions, this may look complicated, but it’s simpler than it looks. We do a simple check to determine if the userAddress
is an array, and if so, we can loop through that array for the address information. Otherwise, the userAddress
will be an object, and we can access the properties as we did previously.
看完函数后,这看起来很复杂,但是比看起来要简单。 我们进行简单的检查以确定userAddress
是否为数组,如果是,则可以遍历该数组以获取地址信息。 否则, userAddress
将是一个对象,我们可以像以前一样访问属性。
使用函数属性 (Using Function Properties)
So what if you need to add a function in an interface? No problem.
那么,如果您需要在接口中添加功能呢? 没问题。
{
id: 1,
name: 'Leanne Graham',
// ... removed to make it shorter / easier to read
company: {
name: 'Romaguera-Crona',
catchPhrase: 'Multi-layered client-server neural-net',
bs: 'harness real-time e-markets',
},
logCompany: (company) => {
console.log(company.name);
},
}
You will notice a new property here, named logCompany
, and it is a function with the parameter of company.
The logCompany
function will log the name
property of the company
object provided.
您将在此处注意到一个名为logCompany
的新属性,它是带有company.
参数的函数company.
logCompany
函数将记录提供的company
对象的name
属性。
How does this affect the User
interface?
这如何影响User
界面?
export interface User {
id: number;
name: string;
username: string;
email: string;
address: Address | Address[];
phone: string;
website: string;
company: Company;
logCompany?: (company: Company) => void;
}
The User
interface has the optional logCompany
property that takes a parameter of company
, which is the Company
interface. This is another example of the benefit of breaking down the objects to their own interfaces. The void
would be the return type of the function. In our case, we are not returning a value, as we logging to the console. If you were to return a value, say, of type string
or boolean
, etc., you would need to specify that return type in the interface.
该User
接口具有可选logCompany
属性,可以取的参数company
,这是Company
的接口。 这是将对象分解为自己的接口的好处的另一个示例。 void
将是函数的返回类型。 在我们的例子中,当我们登录到控制台时,我们没有返回任何值。 如果要返回string
或boolean
等类型的boolean
,则需要在接口中指定该返回类型。
Taking a look at the getUser
function, we can see that as the last line of the function we have user.logCompany?.(user.company);
, which essentially states that if we have the the logCompany
property, we need to execute it as a function with the parameter of user.company
. This will succeed as user.company
is of the definition specified in the Company
interface.
看一下getUser
函数,我们可以看到作为函数的最后一行,我们有user.logCompany?.(user.company);
,它基本上声明如果我们具有logCompany
属性,则需要使用user.company
参数将其作为函数执行。 如果user.company
具有Company
界面中指定的定义,则此操作将成功。
const getUser = (user: User) => {
log.result(`The user's name is ${user.name}`);
log.result(`The user's phone number is ${user.phone}`);
logUserAddress(user.address);
user.logCompany?.(user.company);
};
异步功能 (Asynchronous functions)
As well, you can absolutely use asynchronous functions as a property of an interface. As an example, if you were using a Promise for logCompany
, the Promise would wrap the return type (in our case void)
making the interface look like:
同样,您可以绝对使用异步函数作为接口的属性。 例如,如果您将Promise用于logCompany
,则Promise将包装返回类型(在我们的示例中为void)
使接口看起来像:
logCompany?: (company: Company) => Promise<void>;
Then, of course, we would need to make the getUser
function asynchronous by adding the async
keyword. We would also need to add the await
keyword prior to the user.logCompany?.(user.company);
line.
然后,当然,我们需要通过添加async
关键字使getUser
函数异步。 我们还需要在user.logCompany?.(user.company);
之前添加await
关键字user.logCompany?.(user.company);
线。
const getUser = async (user: User) => {
log.result(`The user's name is ${user.name}`);
log.result(`The user's phone number is ${user.phone}`);
logUserAddress(user.address);
await user.logCompany?.(user.company);
};
结论和其他资源 (Conclusion and Additional Resources)
Thanks for making it this far. I really hope you found this article useful and that the examples improved your understanding of TypeScript interfaces.
感谢您到目前为止。 我真的希望您发现本文有用,并且这些示例可以增进您对TypeScript接口的理解。
Below I have included a GitHub gist with the full code we went over in this article. This can also be copied and used in the TypeScript Playground.
下面,我包含了GitHub要点,以及我们在本文中介绍的完整代码。 这也可以在TypeScript Playground中复制和使用。
If you want some additional resources for understanding TypeScript interfaces, I’d recommend taking a look at TypeScripts official docs. Another excellent resource is Udemy. I’m currently enrolled in the Understanding TypeScript — 2020 Edition and can safely say this is a great resource for all sorts of TypeScript knowledge, including interfaces.
如果您需要其他资源来了解TypeScript接口,建议您看一下TypeScripts官方文档。 另一个极好的资源是Udemy。 我目前已经参加了《理解TypeScript — 2020版》 ,可以放心地说,这是获取各种TypeScript知识(包括界面)的好资源。
I also have a GitHub repository with all my JavaScript and TypeScript tips and tricks. You can clone that repo, and after installing dependencies with npm install
, you can then run npm run ts:interfaces
. You can also modify the interfaces.ts
to satistfy your desire to learn even more and have a local TypeScript playground.
我也有一个GitHub存储库,其中包含我所有JavaScript和TypeScript技巧。 您可以克隆该存储库,并在使用npm install
安装依赖项之后,然后可以运行npm run ts:interfaces
。 您还可以修改interfaces.ts
以满足更多学习需求并拥有本地TypeScript游乐场。
翻译自: https://medium.com/better-programming/a-comprehensive-guide-to-typescript-interfaces-16c5749fac2b
高德地图打字界面