版权声明
- 本文原创作者:谷哥的小弟
- 作者博客地址:http://blog.csdn.net/lfdfhl

TypeScript 接口概述
接口是 TypeScript 中用于描述对象结构的一种语法。对象结构指的是一个对象应该包含哪些属性,以及每个属性分别是什么类型。例如,一个用户对象可能包含用户名、年龄和登录状态;一个商品对象可能包含商品名称、价格和库存状态。接口的作用就是把这类对象结构定义清楚,让 TypeScript 在编码阶段检查对象是否符合要求。
接口和对象类型关系非常密切。前面学习对象类型时,可以直接在变量后面写出对象结构,例如 { username: string; age: number }。这种写法适合对象结构只使用一次的场景。如果同一个对象结构在多个变量、数组、函数参数或函数返回值中反复出现,每次都直接书写对象类型就会比较冗长。接口可以把这段对象结构单独提取出来,并给它起一个名称,后续就可以反复使用这个接口名称。
接口使用 interface 关键字定义。基本写法是先写 interface,再写接口名称,最后在花括号中声明属性名和属性类型。例如,interface User { username: string; age: number; } 表示 User 接口描述的是一个用户对象结构,这个对象必须包含 username 和 age 两个属性,其中 username 是字符串,age 是数字。需要注意的是,接口不是对象本身,也不是具体数据,它主要用于 TypeScript 编写阶段的类型检查。
使用接口时,属性名和属性类型都必须对应。接口中规定了什么属性,真实对象中就应该提供对应属性。如果接口要求属性名是 name,实际对象却写成 username,就会出现结构不匹配的问题。如果接口规定 price 是数字,实际对象却把价格写成字符串 "199",也会出现类型错误。因此,接口既检查对象是否有正确的属性名,也检查每个属性的值是否符合对应类型。
接口中也可以定义可选属性。可选属性的写法是在属性名后面加 ?,例如 address?: string。它表示对象中可以没有 address 属性;如果提供了这个属性,它必须是字符串。可选属性适合描述那些不一定每次都存在的数据字段,例如用户地址、商品描述、头像地址或备注信息。使用可选属性时,也要注意它可能是 undefined,后续访问时应考虑是否已经提供该属性。
接口经常和数组一起使用,用来描述对象数组。比如学生列表、商品列表、用户列表等,通常都是由多个结构相同的对象组成的数组。定义好单个对象的接口后,可以使用 Student[]、Product[] 这样的写法表示对象数组。这样数组中的每一项都必须符合对应接口,某一项如果缺少属性或属性类型不正确,TypeScript 就会提示错误。
接口也常用于函数参数和函数返回值。当函数接收一个对象作为参数时,可以用接口规定参数对象的结构;当函数返回一个对象时,也可以用接口规定返回对象的结构。这样可以让函数的输入和输出更加明确。例如,打印用户信息的函数可以要求参数必须符合 User 接口,创建商品的函数可以声明返回值必须符合 Product 接口。
接口还可以描述对象中的方法。除了普通属性外,对象有时还包含函数形式的方法,例如 sayHello。接口中可以写出方法名称及其函数类型,例如 sayHello: () => void 表示这个方法没有参数,也没有返回值。对于初学者来说,当前阶段可以先重点掌握接口描述普通属性的用法,方法类型只需要能够初步看懂即可。
接口和类型别名都可以描述对象结构。type 可以给对象类型、联合类型等多种类型起别名,interface 更常用于描述对象结构。在 Vue3 前置知识阶段,不需要深入比较二者的高级区别,只要能看懂它们的基本写法,知道二者都可以用于描述对象结构即可。后续阅读 Vue3 代码时,可能会同时看到 type 和 interface,理解它们的基本用途就能减少阅读障碍。
接口名称通常建议使用大驼峰命名法,例如 User、Product、StudentInfo。名称应尽量表达它描述的对象含义,不建议使用 A、B 这类过于随意的名称。掌握接口后,后续学习 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: () => 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>() => 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>