TypeScript 中的type与interface
对于 TypeScript
,有两种定义类型的方法:type
与interface
。
人们常常想知道该使用哪一种,而答案并不是一刀切的。这确实取决于具体情况。有时,其中一种比另一种更好,但在许多情况下,我们可以互换使用它们。
那么,让我们来分析一下type
与interface
有其区别和相似之处。
在本文中,我们将深入探讨这些区别,并总结什么时候使用它们。
type
在js
中有一些基本类型,如字符串、布尔值、数字、数组、元组和枚举。通过 TypeScript
的type
我们定义更多的类型,我们不是在创造新类型,只是给他们起了更友好的名字。这使得我们的代码更容易阅读和理解。
js
type MyNumber = number;
type User = {
id: number;
name: string;
email: string;
}
例如,我们可以为number
创建一个名为MyNumber
的类型别名,因此我们可以直接说MyNumber
,而不是写number
。
我们还可以为User
创建一个类型别名,它描述了用户的数据应该是什么样子。
当人们谈论type
时,他们实际上是在谈论类型别名。这就像给同一组事物赋予不同的名称一样。
interface
在 TypeScript
中,将interface
视为对象必须遵循的一组规则或要求。
js
interface Client {
name: string;
address: string;
}
现在,有另一种方式来表达这些规则。我们可以使用所谓的"类型注释"来更好的解释Client
所包含的属性。
因此,无论我们使用type
还是interface
,我们本质上都是在为Client
定义相同的期望集。这就像为同一组指令赋予两个不同的名称。
type与interface的差异
type
和interface
都是用于定义自定义数据结构和形状,但它们的行为和用法有一些差异。
原始类型
使用type
js
type MyNumber = number;
使用interface
我们不能像直接使用interface
定义原始类型number
。原始类型都是在 TypeScript
中预定义的。
联合类型
使用type
js
type MyUnionType = number | string;
在这里,我们定义了一个MyUnionType
的类型,它可以保存number
或string
类型的值。
使用interface
interface
通常不用于直接表示联合类型。
函数类型
使用type
js
type MyFunctionType = (arg1: number, arg2: string) => boolean;
这定义了一个MyFunctionType
函数的类型,该函数接受两个参数(一个数字和一个字符串)并返回一个布尔值。
使用interface
js
interface MyFunctionInterface {
(arg1: number, arg2: string): boolean;
}
声明合并
使用type
类型别名不支持声明合并。如果多次定义相同的类型别名,将会导致错误。
使用interface
js
interface Person {
name: string;
}
interface Person {
age: number;
}
TypeScript
会自动将这两个Person
接口合并为一个,并且具有name
和age
的属性。
扩展
使用extends
js
interface A { propA: number; }
interface B extends A { propB: string; }
接口B
扩展接口A
,继承propA
属性并添加新属性propB
。
使用&来扩展
js
type AB = A & { propB: string; }
在这里,我们使用交集将类型A
与新属性propB
组合起来创建类型AB。
处理扩展时的冲突
TypeScript
强制扩展时具有相同名称的属性类型匹配:
js
interface A { commonProp: number; }
interface B { commonProp: string; }
interface AB extends A, B { }
// Error: Property 'commonProp' must have the same type in A and B
要解决冲突,我们需要确保类型匹配或使用函数的方法重载。
使用元组类型
使用type
js
type MyTupleType = [number, string];
const tuple: MyTupleType = [42, "hello"];
在这里,我们使用type
定义一个元组类型,然后我们可以创建该元组类型的变量。
使用interface
js
interface MyTupleInterface {
0: number;
1: string;
}
const tuple: MyTupleInterface = [42, "hello"];
我们还可以使用接口定义元组类型,并且用法保持不变。
何时使用type与interface
当需要组合或修改现有结构时,请使用interface
。如果我们正在使用库或创建新库,那么interface
是我们的首选。
它们允许我们合并或扩展声明,从而更轻松地使用现有代码。当我们考虑面向对象编程时,interface
也更具可读性。
当我们需要更强大的功能时,可以选择type
。TypeScript
的类型系统提供了高级工具,例如条件类型、泛型、类型防护等等。
这些功能使我们可以更好地控制类型,帮助我们创建健壮的强类型应用程序。interface
无法提供这些功能。
我们通常可以使用type
与interface
,具体取决于我们的个人喜好。但是,在以下情况下请使用type
:
- 当我们想要为基本数据类型创建新名称(例如"字符串"或"数字")时。
- 定义更复杂的类型(例如联合、元组或函数)时。
- 重载函数时。
- 使用映射类型、条件类型或类型防护等高级功能时。
type
通常更灵活且更具表现力。它们提供了interface
无法比拟的更广泛的高级功能,并且 TypeScript
不断扩展其功能。
我们使用类型别名来自动生成对象类型的 getter
方法,这是interface
无法做到的:
js
type Client = {
name: string;
address: string;
}
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type clientType = Getters<Client>;
// {
// getName: () => string;
// getAddress: () => string;
// }
通过利用映射类型、模板文字和keyof
运算符,我们创建了一个可以自动为任何对象类型生成 getter
方法的类型。
此外,许多开发人员更喜欢使用type
,因为它们与函数式编程范例非常一致。
TypeScript
中类型表达式的丰富性使我们可以更轻松地使用组合和不变性等函数概念,同时保持代码中的类型安全。