总览
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)
测试2
还是上面那个代码 我修改一个写法,测试不会报错 给出解释
function foo(param: Record<string, unknown>) {
}
type Params = {
1: number;
2: string;
}
declare const a: Params
foo(a)
答案 TypeScript
总结
Interface 总体来讲还是适合作为“接口”定义,class中用的比较多。其他场景建议一律用Type Alias,遇到的坑和限制会少一些。