Open SunshowerC opened 5 years ago
type 那个不叫 extend ,那就是类型合并
type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型
我发现大家好像都不是非常喜欢去看文档,其实这种基本的东西根本不用去看 spec ,文档里面就应该有 https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases ,它的设计意图,它的适用场景,它和 interface 的差异在文档里都明明白白的写清楚了,看完就好了
别的公司的东西不好说,微软出的很多东西文档都是写得非常好的,就去看文档就好了
type 那个不叫 extend ,那就是类型合并
type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型 ......
@bolasblack 我当然知道这是类型别名,但是虽然官方定义用途不同,但实际上 interface 和 type 在某些场景下差别不大,是可以通用的,不是么?当然,滥用 type 是不好的。这其实也和 HTML 的语义化有点类同,h1
和 div + css
都能实现同样的样式效果,但是两者的语义是不一样的。
@Weiyu-Chen 那你说说看,为什么滥用 type 是不好的……
其实文档里已经明明白白地写了:
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 ……
我为了把 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
对总结表示不赞同。 interface是接口,type是类型,本身就是两个概念。只是碰巧表现上比较相似。 希望定义一个变量类型,就用type,如果希望是能够继承并约束的,就用interface。 如果你不知道该用哪个,说明你只是想定义一个类型而非接口,所以应该用type。
@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
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)」,这个该如何解释呢?
mark,插眼。 顶上去,日后观察。
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接口不兼容,所以报错
// -------- 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;
了?
感谢。
留个名,刚开始学ts,很多不懂,向各位学习
个人认为两种场景可能会使用到itype而不能用interface:
具体定义数组每个位置的类型
type PetList = [Dog, Pet]
限定具体几个值的基本类型联合类型
type someAnimal = 'dog' | 'cat'
个人认为两种场景可能会使用到itype而不能用interface:
- 具体定义数组每个位置的类型
type PetList = [Dog, Pet]
- 限定具体几个值的基本类型联合类型
type someAnimal = 'dog' | 'cat'
高见
cy typeVSinterface
m
@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;
}
这时 _IGetUserServiceList
和 Data
的shape是不一致的。而加入索引签名后,就可以防止_IGetUserServiceList
被加入string之外的属性,也就保证了和Data
的shape一致。
interface __IGetUserServiceList {
// 增加索引签名后不能再加入boolean属性
[k: string]: string | number;
id: string;
}
interface __IGetUserServiceList {
rich: boolean // error
}
总结一下,type关键字声明的type在创建后是不可变的。而interface在创建后是可变的,但是这种可变性可以通过索引签名来约束。
个人角度,type alias 语法上更像代数数据类型,或许会受到喜欢函数式编程的程序员青睐。例如 type student = [string, number]; 可以看做元组,type student = {name: string, old: number} 可以看做命名元组。 a & b 可以看做 a 和 b 类型的积(product), a | b 可以看做 a 和 b 类型的和(sum)。
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
两者都是为了告诉编译器,如何理解某个字段的结构类型
但两者的定义和使用场景还是有区别的:
所以,在描述带关系的数据结构时,interface应该优先于type被考虑,甚至可以简化思考,直接上interface。而type在一定程度上简化类型描述,例如,type StrOrNum = string | number,后面都可以复用StrOrNum去代表string | number,如果在一个类型描述文件里,string | number这样的类型字段比较多,就可以用type去精简内容。
我觉得创造类型的与集使用interface,使用类型的或集使用type。与集与其说是类型,更应该说是类,只不过这里不需要实例(或者说不需要prototype)。
type 那个不叫 extend ,那就是类型合并 type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型 ......
@bolasblack 我当然知道这是类型别名,但是虽然官方定义用途不同,但实际上 interface 和 type 在某些场景下差别不大,是可以通用的,不是么?当然,滥用 type 是不好的。这其实也和 HTML 的语义化有点类同,
h1
和div + css
都能实现同样的样式效果,但是两者的语义是不一样的。
尽量多用type可以省去很多麻烦
type 那个不叫 extend ,那就是类型合并 type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型 ......
@bolasblack 我当然知道这是类型别名,但是虽然官方定义用途不同,但实际上 interface 和 type 在某些场景下差别不大,是可以通用的,不是么?当然,滥用 type 是不好的。这其实也和 HTML 的语义化有点类同,
h1
和div + css
都能实现同样的样式效果,但是两者的语义是不一样的。尽量多用type可以省去很多麻烦
官网文档说尽可能使用interface
,
mark 我是来学习的
不觉得是好的设计,我认为只保留type就可以了。类型合并这种场景很少,用了也容易滋生bug
从写后端角度。比如:go,给我的感觉是: type 类似于 结构体。 interface 类似于 接口 比较抽象。
个人感觉 type
比 interface
好用一点🙈
如果你是一个人写只给自己看的代码,那可以闭着眼睛只用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
更好一点。
type 那个不叫 extend ,那就是类型合并 type 关键字的产生的东西官方有一个名字 type aliases ,就是类型别名,重点是它是别名不是真正的类型 ......
@bolasblack 我当然知道这是类型别名,但是虽然官方定义用途不同,但实际上 interface 和 type 在某些场景下差别不大,是可以通用的,不是么?当然,滥用 type 是不好的。这其实也和 HTML 的语义化有点类同,
h1
和div + css
都能实现同样的样式效果,但是两者的语义是不一样的。尽量多用type可以省去很多麻烦
官网文档说尽可能使用
interface
,
这个 heuristic 如何来理解呢?这句话我理解起来,官方倒是比较侧重于前面的那句话,就是你喜欢用哪个就用哪个。
interface VS type
大家使用 typescript 总会使用到 interface 和 type,官方规范 稍微说了下两者的区别
明人不说暗话,直接上区别。
相同点
都可以描述一个对象或者函数
interface
type
拓展(extends)与 交叉类型(Intersection Types)
interface 可以 extends, 但 type 是不允许 extends 和 implement 的,但是 type 缺可以通过交叉类型 实现 interface 的 extend 行为,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 与 interface 类型 交叉 。
虽然效果差不多,但是两者语法不同。
interface extends interface
type 与 type 交叉
interface extends type
type 与 interface 交叉
不同点
type 可以而 interface 不行
// 联合类型 interface Dog { wong(); } interface Cat { miao(); }
type Pet = Dog | Cat
// 具体定义数组每个位置的类型 type PetList = [Dog, Pet]
// 当你想获取一个变量的类型时,使用 typeof let div = document.createElement('div'); type B = typeof div
type StringOrNumber = string | number; = (data: T) => void; = [T, T];; = T | { left: Tree, right: Tree };
type Text = string | { text: string };
type NameLookup = Dictionary<string, Person>;
type Callback
type Pair
type Coordinates = Pair
type Tree
interface User { name: string age: number }
interface User { sex: string }
/ User 接口为 { name: string age: number sex: string } /