Interface vs Type Alias
总览
Interface | Type Alias | |
---|---|---|
可用来定义的对象 | object,array,function,class constructor | 可以用来表示所有的类型 |
继承或者扩充性 | 通过 extends 继承 | 通过类型交叉(Intersection Types)扩充 |
同名合并 | 会同名合并 | ❌ |
索引签名 | “静态索引签名” | 索引签名无限制 |
别名 | ❌ | type就是用来作为别名来用的。 |
可用来定义的对象
Interface
1. 定义结构体和数组
interface Person { name: string; age: number; } interface Persons { name: string; age: number; }[] 复制代码
2. 定义函数
interface Func { (hour: string, minute: string): boolean; } 复制代码
3. 定义class constructor
interface ClockConstructor { new (hour: number, minute: number): ClockInterface; } 复制代码
4. 定义混合类型
正如我们之前提到的,接口可以描述现实世界 JavaScript 中存在的丰富类型。 由于 JavaScript 的动态和灵活的特性,您可能偶尔会遇到作为上述某些类型组合工作的对象。 一个这样的例子是一个既作为函数又作为对象的对象,具有附加属性:
interface Counter { (start: number): string; interval: number; reset(): void; } declare let c: Counter c(10); c.reset(); c.interval = 5.0; 复制代码
Type Alias
type这里表达能力拉满,没有什么限制。
1. 定义结构体和数组
type Person = { name: string; age: number; } 复制代码
2. 定义函数
type Func = (hour: string, minute: string) => boolean; 复制代码
3.定义 Class constructor
type SomeConstructor = { new (s: string): SomeObject; }; 复制代码
4. 定义混合类型
type DescribableFunction = { description: string; (someArg: number): boolean; new (s: string): Date; }; 复制代码
其他任何你能想到的type
继承或者扩充性
Interface
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } let square = {} as Square; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0; 复制代码
另外interface也可以继承某个class。这是由于class是有类型的。
class Shape { private color: string } interface PenStroke extends Shape { penWidth: number; } 复制代码
typescript中class相关内容也是很丰富,更多class相关内容后续单独补充。
Type Alias
type alias没有继承,但可以通过类型交叉来合并多个不同的type或者interface。
interface ErrorHandling { success: boolean; error?: { message: string }; } interface ArtistsData { artists: { name: string }[]; } type ArtworksResponse = ArtworksData & ErrorHandling; 复制代码
如果类型无法交叉,则结果为never
type Never = number & string 复制代码
同名合并
Interface
interface Box { height: number; width: number; } interface Box { weight: number; } let box: Box = {height: 4, with: 5, weight: 6} 复制代码
Interface和同名合并性在一些场景下特别有用。例如使用styled-jsx插件。写 会导致ts报错。这种情况下可以通过扩充React namespace中的StyleHTMLAttributes interface即可保证js不会报错。具体做法是在项目中新增d.ts文件。并且新增如下代码块。
namespace React { interface StyleHTMLAttributes<T> extends HTMLAttributes<T> { jsx?: boolean } } 复制代码
namespace和interface一样也存在同名合并,因此我们实际上是扩充了React namespace中StyleHTMLAttributes interface。
interface继承或者同名合并的情况下 如果存在同名属性类型不兼容或者是属性不符合索引签名,则会报错!
interface Colorful { color: string; } interface ColorRgb extends Colorful { opacity: number; color: number; // error! } 复制代码
interface Colorful { color: string; [k: string]: string; } interface Colorful { brightness: number; // error! } 复制代码
索引签名
Interface
interface中的members或者说是属性得是静态的(static)。不是说interface不支持范型,是interface的属性必须得是固定的。它的索引签名(index signature)必须得是静态的,不支持动态索引。
// 我们可以这么定义interface interface Box<T> { height: number; width: number; weight: T } // 甚至可以这样定义 interface Foo { [index: number]: number; [k: `hello-${string}`]: unknown; // typescript4.4新增特性 } 复制代码
但是下面这种写法在interface中是不合法的。
interface GenericType<T extends string, P> { [K in T as`get${K}`]: () => P // error! } 复制代码
Type Alias
type alias中对索引签名没有什么限制。可以最大化利用typescript中各种动态模板能力。
type GenericType<T extends string, P> = { [K in T as`get${Capitalize<K>}`]: () => P } type Hello = GenericType<'hello', number> // getHello: () => number type Foo<T> = { [index: number]: T; [k: `hello-${string}`]: unknown; } const a: Foo<number> = { 2: 2333, 'hello-world': {} } 复制代码
别名
Type Alias
字如其名,Type Alias是type别名。可以指代别的type。interface没有这种能力。 例如
type A = number type B = Array<string> interface Obj { [k: string]: unknown } type C = Obj // 而 interface 没有 “=” 的操作 不能指代其他类型 只有继承 复制代码
小测试
测试1
下面这个代码块会报错,尝试给出正确解释
function foo(param: Record<string, unknown>) { } interface Params { x: number; y: string; } declare const a: Params foo(a) 复制代码
答案 TypeScript playground
测试2
还是上面那个代码 我修改一个写法,测试不会报错 给出解释
function foo(param: Record<string, unknown>) { } type Params = { 1: number; 2: string; } declare const a: Params foo(a) 复制代码
答案 TypeScript
总结
Interface 总体来讲还是适合作为“接口”定义,class中用的比较多。其他场景建议一律用Type Alias,遇到的坑和限制会少一些。
作者:Joe_
链接:https://juejin.cn/post/7022130471135871013