本文详细解析TypeScript中type与interface的区别,涵盖语法定义、继承扩展、同名合并、映射类型及类实现等核心差异。官方建议优先使用interface,仅在需要联合类型、基础类型或映射类型时使用type,以确保类型系统的清晰性与可维护性。
TypeScript类型别名 type
类型别名用于为现有类型创建新名称。通过 type 关键字定义,它不仅能表示基本数据类型,还能灵活定义对象类型、联合类型、元组以及交集类型。
// 基本类型
type userName = string;
// 联合类型
type userId = string | number;
// 数组类型
type arr = number[];
// 对象类型
type Person = {
id: userId; // 可以使用定义类型
name: userName;
age: number;
gender: string;
isWebDev: boolean;
};
// 范型
type Tree<T> = { value: T };
const user: Person = {
id: "901",
name: "椿",
age: 22,
gender: "女",
isWebDev: false,
};
const numbers: arr = [1, 8, 9];Typescript接口 interface
接口是命名数据结构(如对象)的另一种方式。与 type 不同,interface 仅用于描述对象类型,且其声明语法与 type 有所区别。
interface Person {
id: userId;
name: userName;
age: number;
gender: string;
isWebDev: boolean;
}语法不同
interface Point {
x: number;
y: number;
}
interface SetPoint {
(x: number, y: number): void
}
type Point = {
x: number
y: number
}
type SetPoint = (x: number, y: number) => void扩展 (继承) 方式存在差异
在扩展(继承)语法上,interface 使用 extends 关键字,而 type 则使用 & 符号进行类型交叉。
// extends方式不同
interface PartialPointX { x: number }
interface Point extends PartialPointX { y: number }
type PartialPointX = { x: number }
// & 交叉类型
type Point = PartialPointX & { y: number }两者支持互相继承。
// interface 继承 type
type Person{
name:string
}
interface Student extends Person { stuNo: number }
// type 继承 interface
interface Person{
name:string
}
type Student = Person & { stuNo: number }同名合并机制
interface 支持同名合并,而 type 不支持。若系统中存在多个同名的 interface 声明,系统会自动将它们合并为一个接口;若出现同名的 type 声明,则会引发报错。
// interface可以定义多次,并将被视为单个接口
// These two declarations become:
// interface Point { x: number; y: number }
interface Point { x: number }
interface Point { y: number }描述类型范围
对象和函数类型两者均可描述,但 type 还可用于基础类型、联合类型和元组的定义(简单类型推荐使用 type)。
type test = number //基本类型
let num: test = 10
type userOjb = { name:string } // 对象
type getName = () => string // 函数
type data = [number, string] // 元组
type numOrFun = userOjb | getName // 联合类型
interface Common {
name: string
}
interface Person<T> extends Common {
age: T
sex: string
}
type People<T> = {
age: T
sex: string
} & Common
// 联合类型
type P1 = Person<number> | People<number>
// 元组
type P2 = [Person<number>, People<number>]映射类型支持
type 支持计算属性以生成映射类型,而 interface 不支持此特性。
应用场景:当需要基于另一个类型构建新类型且不想复制代码时,映射类型是一个理想选择。
// keys 联合类型
type Keys = 'firstName' | 'surName'
// DudeType 会遍历 keys 所有属性,然后设置为 string 类型
type DudeType = {
[key in Keys]: string
}
const test: DudeType = {
firstName:'Paerl'
surName:'Grz'
}
// 报错
//interface DudeType2 {
// [key in keys]: string
//}
// 带泛型的映射类型
// 在这个例子中,OptionsFlags 会遍历 Type 所有的属性,然后设置为布尔类型
type OptionsFlags<Type> = {
[Property in keyof Type]: boolean
};
type FeatureFlags = {
darkMode: () => void
newUserProfile: () => void
};
type FeatureOptions = OptionsFlags<FeatureFlags>
// type FeatureOptions = {
// darkMode: boolean
// newUserProfile: boolean
// }类实现支持
interface 和 type 均可定义函数类型,但只有 interface 能被 类class(即 class)通过 implements 关键字实现,type 则无法直接实现。
interface SetPerson {
(age: number, sex: string): void
}
type SetPeople = (age: number, sex: string) => void
let setPerson: SetPerson = function (age, sex) {}
let setPeople: SetPeople = function (age, sex) {}
class Config implements SetPerson {
setPerson(age: number, sex: string) {
// do nothing
}
}type 可配合 typeof 使用
type 可以结合 typeof 操作符使用,而 interface 不支持此用法。
class Config {
setPerson(age: number, sex: string) {
// do nothing
}
}
// 获取 Config class 的类型
type T = typeof Config
const C: T = class {
setPerson(age: number, sex: string) {
// do nothing
}
}总结
尽管官方指出几乎接口的所有特性均可通过类型别名实现,但仍建议优先选择 interface。仅在接口无法满足需求时再使用 type 别名。TypeScript 官网在《Preferring Interfaces Over Intersections》中对此有详细说明。
在大多数情况下,对象类型的简单 类型别名 的作用与 接口 非常相似。
interface Foo { prop: string }
type Bar = { prop: string };然而,当需要组合两个或多个类型以生成新类型时,可以选择使用接口扩展这些类型,或者使用类型别名将它们交叉合并,此时差异开始显现。
接口会创建一个单一平面的对象类型来检测属性冲突,这通常至关重要;而交叉类型仅递归合并属性,在某些情况下可能导致 never 类型。
接口的显示效果始终更直观,而交叉类型作为其他交叉类型的一部分时,直观表现较差,容易被误认为是不同基本类型的组合。
接口之间的类型关系会被缓存,而交叉类型被视为组合后的整体。
另一个值得注意的区别是,在检查目标类型之前,会先检查每一个组分。
基于上述原因,建议使用 接口/扩展interface/extends 来扩展类型,而非创建 交叉类型&。
- type Foo = Bar & Baz & {
- someProp: string;
- }
+ interface Foo extends Bar, Baz {
+ someProp: string;
+ }简而言之,接口更符合 JavaScript 对象的工作方式。当出现属性冲突时:
// 接口扩展
interface Sister {
sex: number;
}
interface SisterAn extends Sister {
sex: string;
}
// interface SisterAn
// 接口“SisterAn”错误扩展接口“Sister”。
// 属性“sex”的类型不兼容。
// 不能将类型“string”分配给类型“number”。ts(2430)
// “SisterAn”已声明,但从未使用过。ts(6196)
// 交叉类型
type Sister1 = {
sex: number;
}
type Sister2 = {
sex: string;
}
type SisterAn = Sister1 & Sister2;
// 不报错,此时的 SisterAn 是一个'number & string'类型,也就是 never到此这篇关于Typescript中 type 与 interface 的区别的文章就介绍到这了,更多相关Typescript中 type 与 interface 的区别内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!