发布于 2026-01-06 6 阅读
0

使用 React 学习 TypeScript - 第二部分(接口的定义、意义和实现方法)

使用 React 学习 TypeScript - 第二部分(接口的定义、意义和实现方法)

⚡TL;DR:了解什么是接口、为什么要使用接口以及如何使用接口,可以帮助您编写更多可重用的代码并创建出色的应用程序。

上一篇文章中,我们讨论了类型,包括类型是什么、如何使用以及为什么要使用它们。如果您还没有阅读过,我建议您先阅读一下。

在这篇文章中,我们将了解什么是接口,为什么要关注它,以及如何使用它来编写更多可重用的代码。

我们开始吧。

什么是接口?

在 TypeScript 中,接口是将多个类型注解组合成一个命名注解的核心方式。

它是一种创建新类型的方法,用于描述对象的属性名称及其类型。还记得我们之前介绍的类型(数字、布尔值、字符串等)吗?接口与这些类型相同。

你为什么要关注界面?

对我来说,接口是 TypeScript 语言最棒的特性之一。让我来向你展示它到底有多棒。

让我们从下面的例子开始。

您有一个名为 `function` 的函数renderVideo,它接受video一个参数,您在该参数中标注了视频对象中应该存在的所有属性。

const video = {
  videoId: '',
  title: '',
  description: '',
  thumbnailURL: ''
};

const renderVideo = (video: { videoId: string; title: string; description: string; thumbnailURL: string }): void => {

}
Enter fullscreen mode Exit fullscreen mode

尽管参数注解很长,但上面的代码没有问题,函数参数和返回值都已正确注解。

但是,想象一下,如果您添加了更多可以接受视频的功能。

const video = {
  videoId: '',
  title: '',
  description: '',
  thumbnailURL: ''
};

const renderVideo = (video: { videoId: string; title: string; description: string; thumbnailURL: string }): void => {

}

const getVideoSummary = (video: { videoId: string; title: string; description: string; thumbnailURL: string }): void => {

}

// another function has a `video` parameter
// another one
// and another one
// *shouts* "DJ Khaled"
Enter fullscreen mode Exit fullscreen mode

啊……我不知道你怎么想,但我觉得上面的代码不太好。

代码中存在大量重复的冗长注释,导致代码难以阅读。

现在再想象一下(这里我们做了很多想象),你需要对视频进行以下一项或多项更改:

  • 添加其发布日期作为新属性
  • 重命名thumbnailURLimageURL
  • 将类型videoId从更改string为数字

将这些新更改应用到上面的代码中意味着您需要在很多地方更改所有视频注释。

那工作量太大了,我们不想这样,那么我们该如何解决这个问题呢?

很简单,只需使用一个界面即可。

如何创建界面?

我们可以将这段冗长的视频注释转换成一个名为“ Video.

要创建接口,请使用interface关键字,后跟代表对象的名称,在本例中为Video

名称应该以大写字母开头,就像我们创建 React 组件一样,并且使用一组花括号来声明对象的所有属性、方法和类型。

这就是创建Video界面的方法。

interface Video {
  videoId: string;
  title: string;
  description: string;
  thumbnailURL: string;
}
Enter fullscreen mode Exit fullscreen mode

如何使用界面?

由于接口是一种自定义类型,我们可以像普通类型一样将其注解到变量上。

let item: Video;
Enter fullscreen mode Exit fullscreen mode

通过使用该Video接口,我们可以将前面的示例改写成这样:

interface Video {
  videoId: string;
  title: string;
  description: string;
  thumbnailURL: string;
}

const renderVideo = (video: Video): void => {};
const getVideoSummary = (video: Video): void => {};

// another function that uses a video
// another one
// and another one

const video = {
  videoId: "",
  title: "",
  description: "",
  thumbnailURL: ""
};

renderVideo(video);
Enter fullscreen mode Exit fullscreen mode

这段代码看起来比之前的版本好多了,对吧?

它看起来更简洁,一眼就能看出该参数的类型为Video

最重要的是,我们只需在一个地方(即接口声明)即可添加、删除或重命名属性,或更改属性类型。太棒了!

💡 如果你使用 VS Code(我强烈建议你使用),按住 Ctrlctrl/cmd键并将鼠标悬停在接口名称上,即可查看接口的所有属性名称和类型。 是不是很棒?

应用程序中如何使用界面?

除了使用该Video界面之外,我还创建了另一个界面来表示 YouTube API 响应的结构。

interface YoutubeVideo {
  id: { videoId: string };
  snippet: {
    publishedAt: string;
    title: string;
    description: string;
    thumbnails: { medium: { url: string }; default: { url: string } };
    channelTitle: string;
    channelId: string;
  };
}

interface YoutubeAPIResponse {
  data: { items: YoutubeVideo[] };
}
Enter fullscreen mode Exit fullscreen mode

该接口在函数中使用fetchChannelVideos

export const fetchChannelVideos: (
  args: FetchVideosArguments
) => Promise<Video[]> = async ({
  shouldUseDefaultVideos = false,
  searchQuery
}) => {
 const requestURL = '';
 const response:YoutubeAPIResponse = await axios.get(requestURL);
};
Enter fullscreen mode Exit fullscreen mode

由于接口是一种类型,这意味着 TS 编译器也会对带注释的数据进行错误检查。

通过为 API 响应结构创建接口并将其注释到变量,我们确保不会意外访问不存在的属性。

因此,如果我们尝试访问commentsAPI 响应中未包含的内容,TypeScript 编译器将给我们一个错误。

使用接口编写可重用代码

现在我们已经了解了接口是什么、为什么以及如何使用接口,接下来让我们讨论如何使用接口来编写更多可重用的代码。

假设你有一个组件,用于渲染 YouTube 用户和频道的评论。

首先我们这样写:

interface User { name: string; avatar: string; }
interface Channel { name: string; avatar: string; }

const renderUserComment: (user: User, comment: string) => React.ReactNode = (
  user,
  comment
) => { /* return user comment */ };

const user = { name: "Ana", avatar: "ana.png" };
const channel = { name: "Tedx Talks", avatar: "tedxtalks.png" };

const renderChannelComment: (
  channel: Channel,
  comment: string
) => React.ReactNode = (channel, comment) => {/* return channel comment */};

renderUserComment(user, 'hello there');
renderChannelComment(channel, 'hi, thanks for watching');
Enter fullscreen mode Exit fullscreen mode

用户和频道有两个独立的界面,评论的渲染函数也各不相同,尽管它们执行的是相同的操作,即渲染带有作者的评论。

我们如何重写这段代码以删除重复的代码?

虽然我们可以使用联合类型来表示评论作者可以是 aUser或 a Channel

const renderComment: (author: User|Channel, comment: string) => React.ReactNode = (
  author,
  comment
) => { /* return comment */ };
Enter fullscreen mode Exit fullscreen mode

在这种情况下,最好创建一个界面来代表两种类型的用户。

让我们使用一个名为 的更通用的接口来重写它Author

interface Author { name: string; avatar: string; }

const renderComment: (author: Author, comment: string) => React.ReactNode = (
  author,
  comment
) => { /* return comment */};

const user = { name: "Ana", avatar: "ana.png", github: "analizapandac" };
const channel = { name: "Tedx Talks", avatar: "tedxtalks.png", channelId: "UCsT0YIqwnpJCM-mx7-gSA4Q" };

renderComment(user, 'hello there');
renderComment(channel, 'hi, thanks for watching');
Enter fullscreen mode Exit fullscreen mode

尽管userchannel代表两种不同类型的数据,但它们满足被视为 的要求,author因此nameavatar函数renderComment对这两个对象都有效。

以上示例非常基础,但重点在于我们可以使用通用接口来表示可以与不同函数交互的不同对象。这是利用接口编写更多可重用代码的一种方法。

结语

除了学习界面的原理、原因和方法之外,我还希望能够让您领略到界面的强大功能,以及如何利用界面来创建出色的应用程序。

接口还有很多值得探讨的地方,尤其是如何将它与其他 TypeScript 特性(例如类)结合使用,从而编写出更好的代码。

我将继续学习更高级的TS概念,运用它们创建令人兴奋的新项目,并与大家分享。

希望你也能这样做😊

项目 GitHub 仓库

这是该应用程序的源代码:https://github.com/analizapandac/tedflix

如果你愿意,可以克隆它并在本地修改源代码。

您可以在https://tedflix.netlify.com/上查看已部署的应用程序


以下是前一篇文章:


如果您有任何反馈、建议或下一步方向的指导,请在下方评论区留言。提前感谢!

文章来源:https://dev.to/analizapandac/learning-typescript-with-react-part-2-the-what-why-and-how-of-interfaces-1033