TypeScript接口


版权声明


TypeScript 接口概述

接口是 TypeScript 中用于描述对象结构的一种语法。对象结构指的是一个对象应该包含哪些属性,以及每个属性分别是什么类型。例如,一个用户对象可能包含用户名、年龄和登录状态;一个商品对象可能包含商品名称、价格和库存状态。接口的作用就是把这类对象结构定义清楚,让 TypeScript 在编码阶段检查对象是否符合要求。

接口和对象类型关系非常密切。前面学习对象类型时,可以直接在变量后面写出对象结构,例如 { username: string; age: number }。这种写法适合对象结构只使用一次的场景。如果同一个对象结构在多个变量、数组、函数参数或函数返回值中反复出现,每次都直接书写对象类型就会比较冗长。接口可以把这段对象结构单独提取出来,并给它起一个名称,后续就可以反复使用这个接口名称。

接口使用 interface 关键字定义。基本写法是先写 interface,再写接口名称,最后在花括号中声明属性名和属性类型。例如,interface User { username: string; age: number; } 表示 User 接口描述的是一个用户对象结构,这个对象必须包含 usernameage 两个属性,其中 username 是字符串,age 是数字。需要注意的是,接口不是对象本身,也不是具体数据,它主要用于 TypeScript 编写阶段的类型检查。

使用接口时,属性名和属性类型都必须对应。接口中规定了什么属性,真实对象中就应该提供对应属性。如果接口要求属性名是 name,实际对象却写成 username,就会出现结构不匹配的问题。如果接口规定 price 是数字,实际对象却把价格写成字符串 "199",也会出现类型错误。因此,接口既检查对象是否有正确的属性名,也检查每个属性的值是否符合对应类型。

接口中也可以定义可选属性。可选属性的写法是在属性名后面加 ?,例如 address?: string。它表示对象中可以没有 address 属性;如果提供了这个属性,它必须是字符串。可选属性适合描述那些不一定每次都存在的数据字段,例如用户地址、商品描述、头像地址或备注信息。使用可选属性时,也要注意它可能是 undefined,后续访问时应考虑是否已经提供该属性。

接口经常和数组一起使用,用来描述对象数组。比如学生列表、商品列表、用户列表等,通常都是由多个结构相同的对象组成的数组。定义好单个对象的接口后,可以使用 Student[]Product[] 这样的写法表示对象数组。这样数组中的每一项都必须符合对应接口,某一项如果缺少属性或属性类型不正确,TypeScript 就会提示错误。

接口也常用于函数参数和函数返回值。当函数接收一个对象作为参数时,可以用接口规定参数对象的结构;当函数返回一个对象时,也可以用接口规定返回对象的结构。这样可以让函数的输入和输出更加明确。例如,打印用户信息的函数可以要求参数必须符合 User 接口,创建商品的函数可以声明返回值必须符合 Product 接口。

接口还可以描述对象中的方法。除了普通属性外,对象有时还包含函数形式的方法,例如 sayHello。接口中可以写出方法名称及其函数类型,例如 sayHello: () => void 表示这个方法没有参数,也没有返回值。对于初学者来说,当前阶段可以先重点掌握接口描述普通属性的用法,方法类型只需要能够初步看懂即可。

接口和类型别名都可以描述对象结构。type 可以给对象类型、联合类型等多种类型起别名,interface 更常用于描述对象结构。在 Vue3 前置知识阶段,不需要深入比较二者的高级区别,只要能看懂它们的基本写法,知道二者都可以用于描述对象结构即可。后续阅读 Vue3 代码时,可能会同时看到 typeinterface,理解它们的基本用途就能减少阅读障碍。

接口名称通常建议使用大驼峰命名法,例如 UserProductStudentInfo。名称应尽量表达它描述的对象含义,不建议使用 AB 这类过于随意的名称。掌握接口后,后续学习 Vue3 中的组件数据、列表数据、接口返回数据和函数参数对象时,就能更清楚地理解对象结构和类型检查之间的关系。

案例源码

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>TypeScript 接口</title>
</head>
<body>
  <h1>TypeScript 接口</h1>

  <p>
    本节学习 TypeScript 中的接口。
    接口可以用来描述对象的结构,例如一个用户对象应该有哪些属性,
    每个属性分别是什么类型。
  </p>

  <p>
    前面已经学习过对象类型。
    接口和对象类型之间关系非常密切。
    在正式学习接口语法之前,先理解接口和对象类型的关系,
    可以减少后续学习中的困惑。
  </p>

  <p>
    说明:本节中的代码是 TypeScript 代码,浏览器不能直接运行。
    当前阶段建议使用 TypeScript 官方在线工具
    <a href="https://www.typescriptlang.org/play/" target="_blank">TypeScript Playground</a>
    测试代码。
  </p>

  <hr>

  <h2>一、接口和对象类型的关系</h2>

  <p>
    前面学习对象类型时,我们已经写过类似代码:
  </p>

  <pre><code>let user: { username: string; age: number } = {
  username: "jack",
  age: 18
};</code></pre>

  <p>
    这段代码中,等号左边的:
  </p>

  <pre><code>{ username: string; age: number }</code></pre>

  <p>
    就是对象类型。
    它用来描述 <code>user</code> 这个对象应该有哪些属性,
    以及每个属性应该是什么类型。
  </p>

  <p>
    如果这个对象结构只使用一次,直接写对象类型是可以的。
    但是,如果多个地方都要使用同样的对象结构,
    每次都重复书写这段对象类型,代码就会变得冗长。
  </p>

  <pre><code>let user1: { username: string; age: number } = {
  username: "jack",
  age: 18
};

let user2: { username: string; age: number } = {
  username: "lucy",
  age: 20
};</code></pre>

  <p>
    上面代码中,<code>{ username: string; age: number }</code>
    被重复写了两次。
  </p>

  <p>
    接口就是把这种对象结构单独提取出来,并给它起一个名字。
  </p>

  <pre><code>interface User {
  username: string;
  age: number;
}

let user1: User = {
  username: "jack",
  age: 18
};

let user2: User = {
  username: "lucy",
  age: 20
};</code></pre>

  <p>
    这段代码中,<code>User</code> 接口描述的结构,
    和前面的对象类型 <code>{ username: string; age: number }</code>
    表达的意思基本相同。
  </p>

  <p>
    因此,初学阶段可以这样理解:
  </p>

  <ol>
    <li>对象类型是直接写出来的对象结构描述。</li>
    <li>接口是把对象结构单独提取出来,并起一个名字。</li>
    <li>接口不是对象本身,也不是具体数据。</li>
    <li>接口只是在 TypeScript 中用来检查对象结构是否正确。</li>
  </ol>

  <p>
    简单来说:
    <strong>对象类型更像"现场写结构",接口更像"先定义结构名称,再反复使用"。</strong>
  </p>

  <hr>

  <h2>二、为什么需要接口</h2>

  <p>
    在前端开发中,经常会处理对象数据。
    例如,一个用户对象可能包含用户名、年龄和登录状态。
  </p>

  <pre><code>let user: { username: string; age: number; isLogin: boolean } = {
  username: "jack",
  age: 18,
  isLogin: true
};</code></pre>

  <p>
    这段代码可以正常表示用户对象。
    但是,如果多个地方都要使用同样的用户对象结构,
    每次都重复书写对象类型就不方便。
  </p>

  <pre><code>let user1: { username: string; age: number; isLogin: boolean } = {
  username: "jack",
  age: 18,
  isLogin: true
};

let user2: { username: string; age: number; isLogin: boolean } = {
  username: "lucy",
  age: 20,
  isLogin: false
};</code></pre>

  <p>
    上面代码中,用户对象类型被重复写了两次。
    这种情况下,可以使用接口把对象结构单独定义出来。
  </p>

  <pre><code>interface User {
  username: string;
  age: number;
  isLogin: boolean;
}

let user1: User = {
  username: "jack",
  age: 18,
  isLogin: true
};

let user2: User = {
  username: "lucy",
  age: 20,
  isLogin: false
};</code></pre>

  <p>
    这样写以后,<code>User</code> 就代表用户对象的结构。
    后面只要使用 <code>User</code>,就表示对象必须符合这个结构。
  </p>

  <p>
    接口的作用可以概括为:
    <strong>定义对象应该具备的结构,方便后续重复使用。</strong>
  </p>

  <hr>

  <h2>三、接口的基本语法</h2>

  <p>
    接口使用 <code>interface</code> 关键字定义。
  </p>

  <pre><code>interface 接口名称 {
  属性名: 类型;
  属性名: 类型;
}</code></pre>

  <p>
    例如:
  </p>

  <pre><code>interface Student {
  name: string;
  age: number;
}

let student: Student = {
  name: "张三",
  age: 18
};</code></pre>

  <p>
    这段代码中:
  </p>

  <ol>
    <li><code>interface</code> 用于定义接口。</li>
    <li><code>Student</code> 是接口名称。</li>
    <li><code>name: string</code> 表示对象必须有 <code>name</code> 属性,并且值是字符串。</li>
    <li><code>age: number</code> 表示对象必须有 <code>age</code> 属性,并且值是数字。</li>
    <li><code>student: Student</code> 表示 <code>student</code> 变量必须符合 <code>Student</code> 接口。</li>
  </ol>

  <p>
    接口本身不会生成 JavaScript 代码。
    它主要用于 TypeScript 编写阶段的类型检查。
  </p>

  <hr>

  <h2>四、接口中的属性名必须对应</h2>

  <p>
    接口中的属性名不是随便写的。
    它规定了对象必须具备哪些属性。
  </p>

  <pre><code>interface Student {
  name: string;
  age: number;
}

let student: Student = {
  name: "张三",
  age: 18
};</code></pre>

  <p>
    上面代码是正确的。
    因为 <code>Student</code> 接口要求对象有 <code>name</code> 和 <code>age</code>,
    实际对象中也提供了这两个属性。
  </p>

  <p>
    下面这种写法是错误的:
  </p>

  <pre><code>interface Student {
  name: string;
  age: number;
}

let student: Student = {
  username: "张三",
  age: 18
};</code></pre>

  <p>
    错误原因:
    接口中要求属性名是 <code>name</code>,
    但是实际对象中写成了 <code>username</code>。
  </p>

  <p>
    如果想使用 <code>username</code> 作为属性名,
    接口中也应该写成 <code>username</code>。
  </p>

  <pre><code>interface Student {
  username: string;
  age: number;
}

let student: Student = {
  username: "张三",
  age: 18
};</code></pre>

  <p>
    也就是说:
    <strong>接口中的属性名,要和真实对象中的属性名对应。</strong>
  </p>

  <hr>

  <h2>五、接口中的属性类型必须对应</h2>

  <p>
    接口不仅规定属性名,也规定属性值的类型。
  </p>

  <pre><code>interface Product {
  name: string;
  price: number;
}

let product: Product = {
  name: "键盘",
  price: 199
};</code></pre>

  <p>
    上面代码中:
  </p>

  <ol>
    <li><code>name</code> 必须是字符串。</li>
    <li><code>price</code> 必须是数字。</li>
  </ol>

  <p>
    下面这种写法是错误的:
  </p>

  <pre><code>interface Product {
  name: string;
  price: number;
}

let product: Product = {
  name: "键盘",
  price: "199"
};</code></pre>

  <p>
    错误原因:
    <code>price</code> 在接口中被规定为 <code>number</code>,
    但是实际赋值为字符串 <code>"199"</code>。
  </p>

  <p>
    正确写法:
  </p>

  <pre><code>let product: Product = {
  name: "键盘",
  price: 199
};</code></pre>

  <p>
    记住:
    <strong>接口中的属性名要对应,属性值类型也要对应。</strong>
  </p>

  <hr>

  <h2>六、接口中的可选属性</h2>

  <p>
    有些属性不是必须存在的。
    这种属性可以定义为可选属性。
  </p>

  <p>
    可选属性的写法是在属性名后面加 <code>?</code>。
  </p>

  <pre><code>interface User {
  username: string;
  age: number;
  address?: string;
}</code></pre>

  <p>
    这段代码中:
  </p>

  <ol>
    <li><code>username</code> 是必需属性。</li>
    <li><code>age</code> 是必需属性。</li>
    <li><code>address?</code> 是可选属性。</li>
  </ol>

  <p>
    下面两种写法都正确。
  </p>

  <pre><code>let user1: User = {
  username: "jack",
  age: 18
};

let user2: User = {
  username: "lucy",
  age: 20,
  address: "上海"
};</code></pre>

  <p>
    <code>address?</code> 表示这个属性可以没有。
    但是如果提供了 <code>address</code>,它仍然必须是字符串。
  </p>

  <pre><code>let user3: User = {
  username: "tom",
  age: 22,
  address: 100
};</code></pre>

  <p>
    上面代码是错误的。
    因为 <code>address</code> 如果存在,就必须是字符串。
  </p>

  <hr>

  <h2>七、接口用于对象数组</h2>

  <p>
    接口经常和数组一起使用。
    例如,一个学生列表可以表示为学生对象数组。
  </p>

  <pre><code>interface Student {
  id: number;
  name: string;
  age: number;
}

let students: Student[] = [
  { id: 101, name: "张三", age: 18 },
  { id: 102, name: "李四", age: 19 },
  { id: 103, name: "王五", age: 20 }
];</code></pre>

  <p>
    这段代码中:
  </p>

  <ol>
    <li><code>Student</code> 表示单个学生对象的结构。</li>
    <li><code>Student[]</code> 表示学生对象数组。</li>
    <li>数组中的每一项都必须符合 <code>Student</code> 接口。</li>
  </ol>

  <p>
    如果数组中某一项的属性类型不正确,就会报错。
  </p>

  <pre><code>let students: Student[] = [
  { id: 101, name: "张三", age: 18 },
  { id: 102, name: "李四", age: "19" }
];</code></pre>

  <p>
    错误原因:
    第二个学生对象中的 <code>age</code> 应该是数字,
    但是写成了字符串。
  </p>

  <p>
    对象数组在 Vue3 中非常常见。
    后面使用 <code>v-for</code> 渲染列表时,经常会处理这种数据。
  </p>

  <hr>

  <h2>八、接口用于函数参数</h2>

  <p>
    接口也可以用于函数参数。
    当函数接收一个对象时,可以使用接口描述这个对象的结构。
  </p>

  <pre><code>interface User {
  username: string;
  age: number;
}

function printUser(user: User): void {
  console.log("用户名:" + user.username);
  console.log("年龄:" + user.age);
}

printUser({
  username: "jack",
  age: 18
});</code></pre>

  <p>
    这段代码中:
  </p>

  <ol>
    <li><code>User</code> 接口描述用户对象结构。</li>
    <li><code>printUser</code> 函数的参数 <code>user</code> 必须符合 <code>User</code> 接口。</li>
    <li>调用函数时,传入的对象必须包含 <code>username</code> 和 <code>age</code>。</li>
  </ol>

  <p>
    下面这种调用是错误的:
  </p>

  <pre><code>printUser({
  username: "jack"
});</code></pre>

  <p>
    错误原因:
    参数对象缺少 <code>age</code> 属性。
  </p>

  <p>
    下面这种调用也是错误的:
  </p>

  <pre><code>printUser({
  username: "jack",
  age: "18"
});</code></pre>

  <p>
    错误原因:
    <code>age</code> 应该是数字,
    但是传入了字符串。
  </p>

  <hr>

  <h2>九、接口用于函数返回值</h2>

  <p>
    接口也可以用于函数返回值。
    当函数返回一个对象时,可以使用接口规定返回对象的结构。
  </p>

  <pre><code>interface User {
  username: string;
  age: number;
}

function createUser(): User {
  return {
    username: "jack",
    age: 18
  };
}

let user: User = createUser();</code></pre>

  <p>
    这段代码中:
  </p>

  <ol>
    <li><code>createUser</code> 函数的返回值类型是 <code>User</code>。</li>
    <li>函数必须返回一个符合 <code>User</code> 接口的对象。</li>
    <li>返回对象中必须有 <code>username</code> 和 <code>age</code>。</li>
  </ol>

  <p>
    如果返回对象缺少属性,就会报错。
  </p>

  <pre><code>function createUser(): User {
  return {
    username: "jack"
  };
}</code></pre>

  <p>
    错误原因:
    返回值类型是 <code>User</code>,
    但是返回对象缺少 <code>age</code> 属性。
  </p>

  <p>
    如果返回属性类型不正确,也会报错。
  </p>

  <pre><code>function createUser(): User {
  return {
    username: "jack",
    age: "18"
  };
}</code></pre>

  <p>
    错误原因:
    <code>age</code> 应该是数字,
    但是返回了字符串。
  </p>

  <hr>

  <h2>十、接口中的方法类型</h2>

  <p>
    接口不仅可以描述对象属性,也可以描述对象中的方法。
  </p>

  <pre><code>interface User {
  username: string;
  age: number;
  sayHello: () =&gt; void;
}

let user: User = {
  username: "jack",
  age: 18,
  sayHello: function () {
    console.log("你好,我是 " + this.username);
  }
};

user.sayHello();</code></pre>

  <p>
    这段代码中:
  </p>

  <ol>
    <li><code>username</code> 是字符串属性。</li>
    <li><code>age</code> 是数字属性。</li>
    <li><code>sayHello</code> 是方法。</li>
    <li><code>() =&gt; void</code> 表示这个方法没有参数,也没有返回值。</li>
  </ol>

  <p>
    当前阶段只需要知道:
    接口也可以规定对象中方法的类型。
  </p>

  <p>
    如果暂时觉得方法类型有些抽象,可以先重点掌握接口描述普通属性的用法。
  </p>

  <hr>

  <h2>十一、接口和类型别名的关系</h2>

  <p>
    类型别名和接口都可以描述对象结构。
  </p>

  <p>
    使用类型别名描述对象:
  </p>

  <pre><code>type User = {
  username: string;
  age: number;
};</code></pre>

  <p>
    使用接口描述对象:
  </p>

  <pre><code>interface User {
  username: string;
  age: number;
}</code></pre>

  <p>
    对初学者来说,可以先这样理解:
  </p>

  <ol>
    <li><code>type</code> 可以给很多类型起别名,例如对象类型、联合类型等。</li>
    <li><code>interface</code> 更常用于描述对象结构。</li>
    <li>在 Vue3 前置知识中,二者都要能看懂。</li>
    <li>描述普通对象结构时,使用 <code>type</code> 或 <code>interface</code> 都比较常见。</li>
  </ol>

  <p>
    当前阶段不必深入比较它们的高级区别。
    先掌握它们的基本写法和阅读能力即可。
  </p>

  <hr>

  <h2>十二、接口命名建议</h2>

  <p>
    接口名称建议使用大驼峰命名法。
    大驼峰命名法指的是每个单词首字母大写。
  </p>

  <pre><code>interface User {
  username: string;
  age: number;
}

interface Product {
  name: string;
  price: number;
}

interface StudentInfo {
  name: string;
  score: number;
}</code></pre>

  <p>
    上面这些名称都比较清晰:
  </p>

  <ol>
    <li><code>User</code> 表示用户对象结构。</li>
    <li><code>Product</code> 表示商品对象结构。</li>
    <li><code>StudentInfo</code> 表示学生信息对象结构。</li>
  </ol>

  <p>
    不建议使用过于随意的名称。
  </p>

  <pre><code>interface A {
  username: string;
  age: number;
}</code></pre>

  <p>
    这种写法虽然语法上可以,但名称不清楚。
    其他人看到 <code>A</code> 时,不容易理解它表示什么。
  </p>

  <p>
    对初学者来说,可以先遵守一个简单规则:
    <strong>接口名称尽量表达它描述的对象含义。</strong>
  </p>

  <hr>

  <h2>十三、完整小示例</h2>

  <p>
    下面通过一个商品列表案例,综合演示接口的用法。
  </p>

  <pre><code>// 定义商品接口
interface Product {
  id: number;
  name: string;
  price: number;
  inStock: boolean;
}

// 单个商品对象
let keyboard: Product = {
  id: 101,
  name: "键盘",
  price: 199,
  inStock: true
};

// 商品数组
let productList: Product[] = [
  { id: 101, name: "键盘", price: 199, inStock: true },
  { id: 102, name: "鼠标", price: 99, inStock: true },
  { id: 103, name: "显示器", price: 899, inStock: false }
];

// 打印商品信息
function printProduct(product: Product): void {
  console.log("商品编号:" + product.id);
  console.log("商品名称:" + product.name);
  console.log("商品价格:" + product.price);
  console.log("是否有库存:" + product.inStock);
}

// 创建一个新商品
function createProduct(): Product {
  return {
    id: 104,
    name: "音箱",
    price: 299,
    inStock: true
  };
}

// 使用函数
printProduct(keyboard);

let newProduct: Product = createProduct();
productList.push(newProduct);

// 遍历商品列表
productList.forEach(function (item) {
  printProduct(item);
});

// 错误示例
// keyboard.price = "199";
// productList.push({ id: 105, name: "耳机", price: "399", inStock: true });
// function createWrongProduct(): Product {
//   return { id: 106, name: "摄像头", price: 499 };
// }</code></pre>

  <p>
    这个案例中:
  </p>

  <ol>
    <li><code>Product</code> 是商品接口。</li>
    <li><code>keyboard</code> 是一个商品对象。</li>
    <li><code>productList</code> 是商品对象数组。</li>
    <li><code>printProduct</code> 函数接收一个商品对象。</li>
    <li><code>createProduct</code> 函数返回一个商品对象。</li>
  </ol>

  <p>
    通过接口,商品对象的结构只定义了一次,
    后续变量、数组、函数参数和函数返回值都可以重复使用它。
  </p>

  <hr>

  <h2>十四、本节总结</h2>

  <p>
    本节学习了 TypeScript 接口。
  </p>

  <ol>
    <li>对象类型是直接写出来的对象结构描述。</li>
    <li>接口是把对象结构单独提取出来,并起一个名字。</li>
    <li>接口不是对象本身,也不是运行时数据。</li>
    <li>接口主要用于 TypeScript 编写阶段的类型检查。</li>
    <li>接口使用 <code>interface</code> 关键字定义。</li>
    <li>接口常用于描述对象结构。</li>
    <li>接口可以规定对象应该有哪些属性。</li>
    <li>接口可以规定每个属性的数据类型。</li>
    <li>接口中的属性名要和真实对象属性名对应。</li>
    <li>接口中的属性值类型要和真实数据类型对应。</li>
    <li>接口中可以使用可选属性。</li>
    <li>接口可以用于对象数组。</li>
    <li>接口可以用于函数参数。</li>
    <li>接口可以用于函数返回值。</li>
    <li>接口也可以描述对象中的方法。</li>
    <li><code>type</code> 和 <code>interface</code> 都可以描述对象结构。</li>
    <li>接口名称建议使用大驼峰命名法,并尽量表达对象含义。</li>
  </ol>


</body>
</html>
相关推荐
RobinDevNotes1 小时前
轻量级动画引擎Anime.js迎来V4大版本更新
开发语言·前端·javascript·ecmascript·动画·c4前端
渣波1 小时前
React 性能优化实战:从 memo 到 Hooks 的底层原理与进阶封装
前端·javascript
触底反弹1 小时前
别再 Prop Drilling 了!一文彻底搞懂 React 组件通信的 5 种方案
前端·javascript·react.js
两只羊ovo1 小时前
listToTree 速通:一维数组怎么变出多级菜单?Map 和 reduce 两种全解
前端·javascript
何时梦醒1 小时前
React 进阶必修:彻底搞懂受控组件与非受控组件
前端·javascript·react.js
今日无bug1 小时前
JS 同步与异步:从单线程到 Promise
javascript·promise
sugar__salt2 小时前
深入理解 React useContext:跨层级组件通信的核心利器
前端·javascript·react.js·前端框架·框架
谷哥的小弟3 小时前
TypeScript类型断言
前端·javascript·typescript
weixin_BYSJ19873 小时前
springboot医疗信息管理系统---附源码17465
java·javascript·spring boot·python·django·flask·php