TypeScript 中的类型别名与接口
TypeScript 提供了两种创建自定义数据类型的方法,分别是类型别名和接口。本文将讨论它们之间的异同,以及各自的最佳用例。让我们开始吧!
类型别名
类型别名基本上是任何类型的名称。类型别名不仅可以用来表示原始类型,还可以表示对象类型、联合类型、元组和交集。我们来看一些例子:
type userName = string;
type userId = string | number; // Union type
type arr = number[];
// Object type
type Person = {
id: userId; // We're making use of another type alias here
name: userName;
age: number;
gender: string;
isWebDev: boolean;
};
const user: Person = {
id: 12345,
name: "Tolu",
age: 1,
gender: "female",
isWebDev: true,
};
const numbers: arr = [1, 8, 9];
类型别名使用关键字在前面声明type
。可以将它们视为用于表示特定值的常规 JavaScript 变量。无论何时使用变量名,都会将其求值为其所代表的值。类型别名的工作方式类似。无论何时使用别名注释类型,该别名都将求值为其所代表的类型。与变量一样,您不能多次声明相同的类型别名。TypeScript 永远不会推断别名,您必须显式地对其进行注释。
接口
接口是另一种命名数据结构(例如对象)的方式。接口的声明语法与类型别名不同。让我们将Person
上面的类型别名重写为接口声明:
interface Person {
id: userId;
name: userName;
age: number;
gender: string;
isWebDev: boolean;
}
并集和交集
联合类型由两个或多个其他类型组成,表示一个可以是其中任意一个的值。交集允许我们将多个现有类型组合成一个具有这些类型所有特性的类型。请看以下示例:
type Human = {
name: string;
speaks: boolean;
};
interface Dog {
name: string;
barks: boolean;
}
type HumanAndDog = Human & Dog; // Intersection
type HumanOrDogOrBoth = Human | Dog; // Union
let humanAndDog: HumanAndDog = {
// must have all the properties of Human and Dog
name: "Sparky",
speaks: false,
barks: true,
};
let humanOrDog: HumanOrDogOrBoth = {
// can have the properties of Human or Dog, or both
name: "Tolu",
speaks: true,
};
类型别名和接口可以type
使用联合或交集组合为一个,但不能组合为一个interface
。
元组
元组是一种对固定长度的数组进行类型化的方法,用于计算该数组中的每个项目。
type Mix = [number, string, boolean];
const mix: Mix = [1, "banana", true];
在 TypeScript 中,元组可以用类型声明,但不能用接口声明。不过,元组可以在接口内部使用,如下所示:
interface Mix {
a: [number, string, boolean];
}
声明合并
interface
如果多次声明同名接口,TypeScript 会将它们合并为一个声明,并将它们视为单个接口。这称为声明合并。
interface Person {
name: string;
}
interface Person {
age: number;
}
type Num = numbers;
type Num = string; // Duplicate identifier Num
let user: Person = {
// has the properties of both instances of Person
name: "Tolu",
age: 0,
};
声明合并仅适用于接口。如果多次声明相同的类型别名,TypeScript 将会报错。
扩展
类型别名和接口都可以扩展。但是,语法有所不同。派生类型别名或接口具有其基类型别名或接口的所有属性和方法,并且还可以定义其他成员。
类型别名可以使用与号扩展另一个类型别名:
type A = { x: number };
type B = A & { y: string };
类型别名也可以扩展接口:
interface A {
x: number;
}
type B = A & { y: string };
接口可以使用关键字扩展类型别名extends
:
type A = {
x: number;
};
interface B extends A {
y: string;
}
接口可以像扩展类型别名一样扩展其他接口。接口还可以扩展多个接口,多个接口之间用逗号分隔。
interface A {
x: string;
y: number;
}
interface B {
z: boolean;
}
interface C extends A, B {
breed: string;
}
let c: C = {
x: "Sparky",
y: 4,
z: true,
};
扩展类的接口
TypeScript 允许接口扩展类。当接口扩展类时,它会继承基类的成员,包括私有成员和公共成员,但不继承它们的实现。这意味着,当你创建一个扩展了带有私有成员的类的接口时,只有该类或其子类才能实现该接口。这样,你就可以将接口的使用限制在其扩展的类或其子类中。
class Base {
greetFriend() {
console.log(`Hello!`);
}
}
// Interface extending the Base class
interface Derived extends Base {
giveGist(): void;
}
// New class that extends Base class and implements the Derived interface
class NewClass extends Base implements Derived {
giveGist() {
console.log("I saw this the other day...");
}
}
const c = new NewClass();
c.greetFriend(); // Hello!
c.giveGist(); // I saw this the other day...
工具
TypeScript 支持基于类的面向对象编程。因此,它允许类使用implements
关键字实现类型别名和接口。如果类未能正确实现该关键字,则会抛出错误。
// Interface being implemented by a class
interface A {
x: number;
y: number;
}
class SomeClass implements A {
x = 1;
y = 2;
}
// Type alias being implemented by a class
type B = {
x: number;
y: number;
};
class SomeClass2 implements B {
x = 1;
y = 2;
}
一个类也可以实现多个接口,接口之间用逗号分隔,例如class A implements B, C {}
。请注意,类不能实现或扩展表示联合类型的类型别名:
type C = { x: number } | { y: number };
// ERROR: A class can only implement an object type or intersection of object types with statically known members.
class SomeClass3 implements C {
x = 1;
y = 2;
}
重要的是要理解,implements 子句只是检查该类是否可以被视为接口类型。它根本不会改变类或其方法的类型。一个常见的错误是假设 implements 子句会改变类的类型——其实不会!
—— TypeScript 官方文档
我应该使用哪一个?
类型别名和接口非常相似,您可以自由选择。我个人在定义原始类型、并集、交集、函数或元组类型时使用类型别名。然而,在定义对象类型或利用声明合并时,我会使用接口。
结论
我们已经看到了类型别名和接口之间的区别和相似之处。
- 类型别名和接口都可以用于描述对象的形状或函数签名。但语法有所不同。
- 声明合并仅适用于接口,而不适用于类型别名。
- 您不能使用关键字声明并集、交集或元组
interface
。但是,您可以在接口中使用它们。 - 类可以实现类型别名和接口,但不能实现表示联合类型的类型别名。
希望您能从本文中有所收获。如有任何问题或补充信息,请在评论区留言。
感谢阅读!
鏂囩珷鏉ユ簮锛�https://dev.to/toluagboola/type-aliases-vs-interfaces-in-typescript-3ggg