Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

typescript 中的 interface 和 type 到底有什么区别? #7

Open
SunshowerC opened this issue Dec 29, 2018 · 29 comments
Open

typescript 中的 interface 和 type 到底有什么区别? #7

SunshowerC opened this issue Dec 29, 2018 · 29 comments

Comments

@SunshowerC
Copy link
Owner

SunshowerC commented Dec 29, 2018

interface VS type

大家使用 typescript 总会使用到 interface 和 type,官方规范 稍微说了下两者的区别

  • An interface can be named in an extends or implements clause, but a type alias for an object type literal cannot.
  • An interface can have multiple merged declarations, but a type alias for an object type literal cannot.
    但是没有太具体的例子。

明人不说暗话,直接上区别。

相同点

都可以描述一个对象或者函数

interface

interface User {
  name: string
  age: number
}

interface SetUser {
  (name: string, age: number): void;
}

type

type User = {
  name: string
  age: number
};

type SetUser = (name: string, age: number): void;

拓展(extends)与 交叉类型(Intersection Types)

interface 可以 extends, 但 type 是不允许 extends 和 implement 的,但是 type 缺可以通过交叉类型 实现 interface 的 extend 行为,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 与 interface 类型 交叉 。

虽然效果差不多,但是两者语法不同

interface extends interface

interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

type 与 type 交叉

type Name = { 
  name: string; 
}
type User = Name & { age: number  };

interface extends type

type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}

type 与 interface 交叉

interface Name { 
  name: string; 
}
type User = Name & { 
  age: number; 
}

不同点

type 可以而 interface 不行

  • type 可以声明基本类型别名,联合类型,元组等类型
// 基本类型别名
type Name = string

// 联合类型
interface Dog {
    wong();
}
interface Cat {
    miao();
}

type Pet = Dog | Cat

// 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
  • type 语句中还可以使用 typeof 获取实例的 类型进行赋值
// 当你想获取一个变量的类型时,使用 typeof
let div = document.createElement('div');
type B = typeof div
  • 其他骚操作
type StringOrNumber = string | number;  
type Text = string | { text: string };  
type NameLookup = Dictionary<string, Person>;  
type Callback<T> = (data: T) => void;  
type Pair<T> = [T, T];  
type Coordinates = Pair<number>;  
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };

interface 可以而 type 不行

interface 能够声明合并

interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/*
User 接口为 {
  name: string
  age: number
  sex: string 
}
*/

总结

一般来说,如果不清楚什么时候用interface/type,能用 interface 实现,就用 interface , 如果不能就用 type 。其他更多详情参看 官方规范文档

@bolasblack
Copy link

bolasblack commented Jan 14, 2019

type 那个不叫 extend ,那就是类型合并

type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型

我发现大家好像都不是非常喜欢去看文档,其实这种基本的东西根本不用去看 spec ,文档里面就应该有 https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases ,它的设计意图,它的适用场景,它和 interface 的差异在文档里都明明白白的写清楚了,看完就好了

别的公司的东西不好说,微软出的很多东西文档都是写得非常好的,就去看文档就好了

@SunshowerC
Copy link
Owner Author

SunshowerC commented Jan 14, 2019

type 那个不叫 extend ,那就是类型合并

type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型
......

@bolasblack 我当然知道这是类型别名,但是虽然官方定义用途不同,但实际上 interface 和 type 在某些场景下差别不大,是可以通用的,不是么?当然,滥用 type 是不好的。这其实也和 HTML 的语义化有点类同,h1div + css 都能实现同样的样式效果,但是两者的语义是不一样的。

@bolasblack
Copy link

@weiyu-chen 那你说说看,为什么滥用 type 是不好的……

@bolasblack
Copy link

其实文档里已经明明白白地写了:

A second more important difference is that type aliases cannot be extended or implemented from (nor can they extend/implement other types).

这就是我为什么在开头要强调 type 的那个不叫 extend ,因为那个真的不是 extend ……

@SunshowerC
Copy link
Owner Author

我为了把 type 类比 interface 的 extend 行为,顺手就用 extend 了。确实,用 extend 来描述 类型别名确实不严谨,感谢勘误。
另外,“为什么滥用 type 是不好的”,我个人认为,除了type 和 interface 在行为上确实有一定差异之外,还有的是上述我提的语义化,能用 interface 描述一个类型,尽量不要用 type 描述。
官方文档也是建议尽可能使用 interface

you should always use an interface over a type alias if possible.
On the other hand, if you can’t express some shape with an interface and you need to use a union or tuple type, type aliases are usually the way to go.

还有什么问题或者意见的欢迎提出来,共同探讨。 @bolasblack

@trlanfeng
Copy link

trlanfeng commented Jan 17, 2019

对总结表示不赞同。
interface是接口,type是类型,本身就是两个概念。只是碰巧表现上比较相似。
希望定义一个变量类型,就用type,如果希望是能够继承并约束的,就用interface。
如果你不知道该用哪个,说明你只是想定义一个类型而非接口,所以应该用type。

@SunshowerC
Copy link
Owner Author

SunshowerC commented Jan 18, 2019

@trlanfeng 这么理解倒也不能说错,但是 type/interface 到底怎么用这可能真的见仁见智了。 type 和 interface 除了官方定义不同之外,很多功能其实 用 type 和 interface 从结果上看没多大区别。

例如这篇文章interface-vs-type-alias就介绍说 type 和 interface 只要和团队保持统一就好,除了特定场景外,没有什么是必须用 interface/type 的。

  • type aliases can act sort of like interfaces, however, there are 3 important differences ( union types, declaration merging)
  • use whatever suites you and your team, just be consistent
  • always use interface for public API's definition when authoring a library or 3rd party ambient type definitions
  • consider using type for your React Component Props and State

@yinshuxun
Copy link

interface StringMap {
  [key: string]: string;
}
interface A {
  key?: string;
}

type B = {
  key?: string;
};
const a: A = { key: "1" };
const b: B = { key: "1" };
const c: StringMap = b;
const d: StringMap = a;

这段代码,执行const d的时候会有错误。提示「
不能将类型“A”分配给类型“StringMap”。 类型“A”中缺少索引签名。ts(2322)」,这个该如何解释呢?

@xgqfrms
Copy link

xgqfrms commented Aug 24, 2019

image

@yanyang1116
Copy link

mark,插眼。
顶上去,日后观察。

@Liuyll
Copy link

Liuyll commented Feb 9, 2020

interface StringMap {
  [key: string]: string;
}
interface A {
  key?: string;
}

type B = {
  key?: string;
};
const a: A = { key: "1" };
const b: B = { key: "1" };
const c: StringMap = b;
const d: StringMap = a;

这段代码,执行const d的时候会有错误。提示「
不能将类型“A”分配给类型“StringMap”。 类型“A”中缺少索引签名。ts(2322)」,这个该如何解释呢?

写过c或者go这类语言就很容易明白type了
type就是type alias 也就是 go 里的type:type S = string;var s S
把B类型给b,再赋StringMap类型,实际上B就是一个别名
而A类型不同,A是一个接口,它实际上存在,并且与拥有索引签名的StringMap接口不兼容,所以报错

@jsjzh
Copy link

jsjzh commented Apr 16, 2020

// -------- base --------

type Data = Record<string, string | number>;

function fn(data: Data) {}

// -------- step one --------

type IGetUserServiceList = {
  id: string;
};

let fooData: IGetUserServiceList = {
  id: '12345',
};

fn(fooData);

// -------- step two --------

interface _IGetUserServiceList {
  id: string;
}

let _fooData: _IGetUserServiceList = {
  id: '12345',
};

// error
// 类型“_IGetUserServiceList”中缺少索引签名
fn(_fooData);

// 改为如下即可
interface __IGetUserServiceList {
  // 需要增加索引签名
  [k: string]: string | number;
  id: string;
}

借楼打扰,关于上面的这个问题,可以解释一下么,使用 type 和 interface 定义的都是相同的对象,但是使用 interface 的时候会提示缺少索引签名,难道是 type 定义对象的时候,已经默认给予了 [k:string]: string | number; 了?

感谢。

@negeafei
Copy link

留个名,刚开始学ts,很多不懂,向各位学习

@Alcantara6
Copy link

Alcantara6 commented May 24, 2020

个人认为两种场景可能会使用到itype而不能用interface:

  1. 具体定义数组每个位置的类型
    type PetList = [Dog, Pet]

  2. 限定具体几个值的基本类型联合类型
    type someAnimal = 'dog' | 'cat'

@alowkeyguy
Copy link

个人认为两种场景可能会使用到itype而不能用interface:

  1. 具体定义数组每个位置的类型
    type PetList = [Dog, Pet]
  2. 限定具体几个值的基本类型联合类型
    type someAnimal = 'dog' | 'cat'

高见

@Eating-Eating
Copy link

cy typeVSinterface

@HomyeeKing
Copy link

m

@imgss
Copy link

imgss commented Nov 3, 2020

@jsjzh 我理解是这样的,借用官方文档的话
interface 可以Adding new fields to an existing interface,而一个type,A type cannot be changed after being created。

就你的例子来说,IGetUserServiceList 由type创建,它的 shape已经固定,但是_IGetUserServiceList完全可以在后面再加一个boolean属性:

interface _IGetUserServiceList {
  rich: boolean;
}

从而导致_IGetUserServiceList最终的shape是下面这样:

interface _IGetUserServiceList {
  rich: boolean;
  id: string;
}

这时 _IGetUserServiceListData 的shape是不一致的。而加入索引签名后,就可以防止_IGetUserServiceList被加入string之外的属性,也就保证了和Data的shape一致。

interface __IGetUserServiceList {
  // 增加索引签名后不能再加入boolean属性
  [k: string]: string | number;
  id: string;
}
interface __IGetUserServiceList {
  rich: boolean  // error
}

总结一下,type关键字声明的type在创建后是不可变的。而interface在创建后是可变的,但是这种可变性可以通过索引签名来约束。

@SyMind
Copy link

SyMind commented Dec 14, 2020

个人角度,type alias 语法上更像代数数据类型,或许会受到喜欢函数式编程的程序员青睐。例如 type student = [string, number]; 可以看做元组,type student = {name: string, old: number} 可以看做命名元组。 a & b 可以看做 a 和 b 类型的积(product), a | b 可以看做 a 和 b 类型的和(sum)。

@hjwzzz
Copy link

hjwzzz commented Feb 23, 2021

interface StringMap {
  [key: string]: string;
}
interface A {
  key?: string;
}

type B = {
  key?: string;
};
const a: A = { key: "1" };
const b: B = { key: "1" };
const c: StringMap = b;
const d: StringMap = a;

这段代码,执行const d的时候会有错误。提示「
不能将类型“A”分配给类型“StringMap”。 类型“A”中缺少索引签名。ts(2322)」,这个该如何解释呢?

type TestA = A extends StringMap ? A : never

// TestA = never

type TestB = B extends StringMap ? B : never

// TestB = B

@forcs
Copy link

forcs commented Mar 12, 2021

两者都是为了告诉编译器,如何理解某个字段的结构类型

但两者的定义和使用场景还是有区别的:

  • interface 是一种关系结构的描述,里面可以包含属性和方法,可派生
  • type 是一种表达式,所以也可以说是一种aliase,可以使用一些表达式的操作符,并且通过这些操作符实现和interface近似等价的关系描述

所以,在描述带关系的数据结构时,interface应该优先于type被考虑,甚至可以简化思考,直接上interface。而type在一定程度上简化类型描述,例如,type StrOrNum = string | number,后面都可以复用StrOrNum去代表string | number,如果在一个类型描述文件里,string | number这样的类型字段比较多,就可以用type去精简内容。

@gonlyk
Copy link

gonlyk commented Sep 30, 2021

我觉得创造类型的与集使用interface,使用类型的或集使用type。与集与其说是类型,更应该说是类,只不过这里不需要实例(或者说不需要prototype)。

@akriafly
Copy link

type 那个不叫 extend ,那就是类型合并
type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型
......

@bolasblack 我当然知道这是类型别名,但是虽然官方定义用途不同,但实际上 interface 和 type 在某些场景下差别不大,是可以通用的,不是么?当然,滥用 type 是不好的。这其实也和 HTML 的语义化有点类同,h1div + css 都能实现同样的样式效果,但是两者的语义是不一样的。

尽量多用type可以省去很多麻烦

@ChenKun1997
Copy link

type 那个不叫 extend ,那就是类型合并
type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型
......

@bolasblack 我当然知道这是类型别名,但是虽然官方定义用途不同,但实际上 interface 和 type 在某些场景下差别不大,是可以通用的,不是么?当然,滥用 type 是不好的。这其实也和 HTML 的语义化有点类同,h1div + css 都能实现同样的样式效果,但是两者的语义是不一样的。

尽量多用type可以省去很多麻烦

官网文档说尽可能使用interface,
image

@JuctTr
Copy link

JuctTr commented Aug 5, 2022

mark
我是来学习的

@happy-little-one
Copy link

不觉得是好的设计,我认为只保留type就可以了。类型合并这种场景很少,用了也容易滋生bug

@localhostjason
Copy link

从写后端角度。比如:go,给我的感觉是:
type 类似于 结构体。
interface 类似于 接口 比较抽象。

@wmasfoe
Copy link

wmasfoe commented Mar 16, 2023

个人感觉 typeinterface 好用一点🙈

@tonny1983
Copy link

tonny1983 commented Feb 4, 2024

如果你是一个人写只给自己看的代码,那可以闭着眼睛只用interface。如果写的代码会给别人看,那就用type。下列所谓的interface合并功能明显是违反OCP的:

interface User {
  name: string,
}

/*
User interface itself now has been changed!
*/
interface User {
  sex: string,
}

这跟js中从头到尾用一个变量a来接收所有值是一个性质——只要不用维护,怎么写都是你的自由。

事实上,interface的能力比type强大(除了起别名),这就是为什么文档中说:

If you would like a heuristic, use interface until you need to use features from type.

但是如果用不到这些强大的能力,那么还是用type更好一点。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests