TypeScript中的这些语句(接口与类型)有什么区别?
interface X {
a: number
b: string
}
type X = {
a: number
b: string
};
TypeScript中的这些语句(接口与类型)有什么区别?
interface X {
a: number
b: string
}
type X = {
a: number
b: string
};
当前回答
来自官方文件
类型别名和接口之间的差异类型别名和接口非常相似,在许多情况下,您可以在它们之间自由选择。接口的几乎所有功能都可以在类型中使用,关键区别在于,与总是可扩展的接口相比,不能重新打开类型来添加新的财产。
其他回答
其他答案很棒!Type可以做但Interface不能做的其他事情很少
可以在类型中使用联合
type Name = string | { FullName: string };
const myName = "Jon"; // works fine
const myFullName: Name = {
FullName: "Jon Doe", //also works fine
};
在类型中迭代联合财产
type Keys = "firstName" | "lastName";
type Name = {
[key in Keys]: string;
};
const myName: Name = {
firstName: "jon",
lastName: "doe",
};
类型中的交集(但是,在带有扩展的接口中也支持)
type Name = {
firstName: string;
lastName: string;
};
type Address = {
city: string;
};
const person: Name & Address = {
firstName: "jon",
lastName: "doe",
city: "scranton",
};
与接口相比,这种类型也不是后来才引入的,根据最新发布的TS类型,它几乎可以做任何接口可以做的事情,而且更多!
*除了声明合并(个人观点:很好,它在类型中不受支持,因为它可能导致代码不一致)
2021 3月更新:更新的TypeScript手册(也在nju-clc中提到下面的答案)有一节“接口与类型别名”,解释了区别。
原始答案(2016)
根据(现已存档)TypeScript语言规范:
与总是引入命名对象类型的接口声明不同,类型别名声明可以引入任何类型的名称,包括基元类型、联合类型和交集类型。
该规范还提到:
接口类型与对象类型的类型别名有许多相似之处但是由于接口类型提供了更多的功能通常优选键入别名。例如,接口类型接口点{x: 数量;y: 数量;}可以写成类型别名类型点={x: 数量;y: 数量;};但是,这样做意味着失去以下功能:接口可以在extends或implements子句中命名,但对象类型文本的类型别名不能在TS 2.7之后为true。接口可以有多个合并声明,但对象类型文本的类型别名不能。
在我的日常发展中,当我不知道该选择哪一个时,我会使用这个作弊表。
有关更多信息,请阅读我的博客:https://medium.com/@magenta2127/use-which-interface-or-type-alias-in-typescript-bdfaf2e882ae
索引的差异。
interface MyInterface {
foobar: string;
}
type MyType = {
foobar: string;
}
const exampleInterface: MyInterface = { foobar: 'hello world' };
const exampleType: MyType = { foobar: 'hello world' };
let record: Record<string, string> = {};
record = exampleType; // Compiles
record = exampleInterface; // Index signature is missing
相关问题:类型中缺少索引签名(仅在接口上,而不是在类型别名上)
因此,如果您想为对象编制索引,请考虑这个示例
看看这个问题和这个关于违反利斯科夫原则的问题
评估中的差异
当FirstLevelType为接口时,请查看ExtendeFirst的结果类型
/**
* When FirstLevelType is interface
*/
interface FirstLevelType<A, Z> {
_: "typeCheck";
};
type TestWrapperType<T, U> = FirstLevelType<T, U>;
const a: TestWrapperType<{ cat: string }, { dog: number }> = {
_: "typeCheck",
};
// { cat: string; }
type ExtendFirst = typeof a extends FirstLevelType<infer T, infer _>
? T
: "not extended";
当FirstLevelType为类型时,请查看ExtendeFirst的结果类型:
/**
* When FirstLevelType is type
*/
type FirstLevelType<A, Z>= {
_: "typeCheck";
};
type TestWrapperType<T, U> = FirstLevelType<T, U>;
const a: TestWrapperType<{ cat: string }, { dog: number }> = {
_: "typeCheck",
};
// unknown
type ExtendFirst = typeof a extends FirstLevelType<infer T, infer _>
? T
: "not extended";
何时使用类型?
通用转换
将多个类型转换为单个泛型类型时,请使用该类型。
例子:
type Nullable<T> = T | null | undefined
type NonNull<T> = T extends (null | undefined) ? never : T
类型别名
我们可以使用该类型为长类型或复杂类型创建别名,这些类型既难以阅读,又不方便反复键入。
例子:
type Primitive = number | string | boolean | null | undefined
创建这样的别名使代码更加简洁易读。
类型捕获
当类型未知时,使用该类型捕获对象的类型。
例子:
const orange = { color: "Orange", vitamin: "C"}
type Fruit = typeof orange
let apple: Fruit
在这里,我们得到了未知类型的橙子,称其为水果,然后使用水果创建一个新型的安全对象苹果。
何时使用界面?
多态性
接口是实现数据形状的契约。使用该接口明确表示它将被实现并用作关于如何使用对象的约定。
例子:
interface Bird {
size: number
fly(): void
sleep(): void
}
class Hummingbird implements Bird { ... }
class Bellbird implements Bird { ... }
尽管您可以使用类型来实现这一点,但Typescript更多地被视为一种面向对象的语言,并且接口在面向对象语言中具有特殊的地位。当您在团队环境中工作或为开源社区做出贡献时,使用界面更容易阅读代码。对于来自其他面向对象语言的新程序员来说也很容易。
官方的Typescript文档还说:
…我们建议尽可能在类型别名上使用接口。
这也表明该类型更适合于创建类型别名,而不是创建类型本身。
声明合并
您可以使用接口的声明合并功能向已声明的接口添加新的财产和方法。这对于第三方库的环境类型声明很有用。当第三方库缺少某些声明时,可以使用相同的名称再次声明接口,并添加新的财产和方法。
例子:
我们可以扩展上述Bird接口以包含新的声明。
interface Bird {
color: string
eat(): void
}
就是这样!记住什么时候使用比迷失在两者之间的细微差别中更容易。