TypeScript中的这些语句(接口与类型)有什么区别?

interface X {
    a: number
    b: string
}

type X = {
    a: number
    b: string
};

当前回答

想加上我的2美分;

我曾经是“界面爱好者”(除了联合、交叉等以外,我更喜欢界面而不是类型)。。。直到我开始使用类型“any key value object”,即Record<string,unknown>

如果键入“任意键值对象”:

function foo(data: Record<string, unknown>): void {
  for (const [key, value] of Object.entries(data)) {
    // whatever
  }
}

如果你使用接口,你可能会走到死胡同

interface IGoo {
  iam: string;
}

function getGoo(): IGoo {
  return { iam: 'an interface' };
}

const goo = getGoo(); 

foo(goo); // ERROR
// Argument of type 'IGoo' is not assignable to parameter of type 
// 'Record<string, unknown>'.
//  Index signature for type 'string' is missing in type 
// 'IGoo'.ts(2345)

虽然打字就像一个符咒:

type Hoo = {
  iam: string;
};

function getHoo(): Hoo {
  return { iam: 'a type' }; 
}

const hoo = getHoo(); 

foo(hoo); // works

这个特定的用例——IMO——产生了不同。

其他回答

2021 3月更新:更新的TypeScript手册(也在nju-clc中提到下面的答案)有一节“接口与类型别名”,解释了区别。


原始答案(2016)

根据(现已存档)TypeScript语言规范:

与总是引入命名对象类型的接口声明不同,类型别名声明可以引入任何类型的名称,包括基元类型、联合类型和交集类型。

该规范还提到:

接口类型与对象类型的类型别名有许多相似之处但是由于接口类型提供了更多的功能通常优选键入别名。例如,接口类型接口点{x: 数量;y: 数量;}可以写成类型别名类型点={x: 数量;y: 数量;};但是,这样做意味着失去以下功能:接口可以在extends或implements子句中命名,但对象类型文本的类型别名不能在TS 2.7之后为true。接口可以有多个合并声明,但对象类型文本的类型别名不能。

在typescript中,建议使用“interface”而不是“type”。

“type”用于创建类型别名。您不能使用“接口”执行此操作。type Data=字符串然后,您可以使用“数据”代替字符串const name:string=“Yilmaz”const name:Data=“Yilmaz”

别名非常有用,尤其是在处理泛型类型时。

声明合并:可以合并接口,但不能合并类型。界面人员{name:字符串;}界面人员{年龄:数字;}//我们必须提供两个人的财产常量名称:人={名称:“Yilmaz”,年龄:30岁};函数式编程用户使用“类型”,面向对象编程用户选择“接口”您不能在接口上计算或计算财产,而是在类型中。type Fullname=“name”|“lastname”类型人员={[key in Keys]:字符串}常量名称:人={名字:“Yilmaz”,姓氏:“Bingol”}

想加上我的2美分;

我曾经是“界面爱好者”(除了联合、交叉等以外,我更喜欢界面而不是类型)。。。直到我开始使用类型“any key value object”,即Record<string,unknown>

如果键入“任意键值对象”:

function foo(data: Record<string, unknown>): void {
  for (const [key, value] of Object.entries(data)) {
    // whatever
  }
}

如果你使用接口,你可能会走到死胡同

interface IGoo {
  iam: string;
}

function getGoo(): IGoo {
  return { iam: 'an interface' };
}

const goo = getGoo(); 

foo(goo); // ERROR
// Argument of type 'IGoo' is not assignable to parameter of type 
// 'Record<string, unknown>'.
//  Index signature for type 'string' is missing in type 
// 'IGoo'.ts(2345)

虽然打字就像一个符咒:

type Hoo = {
  iam: string;
};

function getHoo(): Hoo {
  return { iam: 'a type' }; 
}

const hoo = getHoo(); 

foo(hoo); // works

这个特定的用例——IMO——产生了不同。

就编译速度而言,组合接口的性能优于类型交集:

[…]接口创建检测属性冲突的单个平面对象类型。这与交叉点类型不同,在交叉点类型中,在对照有效类型进行检查之前,检查每个组成部分。接口之间的类型关系也被缓存,而不是交叉类型。

资料来源:https://github.com/microsoft/TypeScript/wiki/Performance#preferring-交叉口上的接口

索引的差异。

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";