Typescript的interface、class和abstract class
<p>interface,class,和abstract class这3个概念,既有联系,又有区别,本文尝试着结合官方文档来阐述这三者之间的关系。</p><h3 id="1-declaration-merging">1. Declaration Merging</h3>
<table>
<thead>
<tr>
<th>Declaration Type</th>
<th>Namespace</th>
<th>Type</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Namespace</td>
<td>X</td>
<td></td>
<td>X</td>
</tr>
<tr>
<td>Class</td>
<td></td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td>Enum</td>
<td></td>
<td>X</td>
<td>X</td>
</tr>
<tr>
<td>Interface</td>
<td></td>
<td>X</td>
<td></td>
</tr>
<tr>
<td>Type Alias</td>
<td></td>
<td></td>
<td>X</td>
</tr>
<tr>
<td>Function</td>
<td></td>
<td></td>
<td>X</td>
</tr>
<tr>
<td>Variable</td>
<td></td>
<td></td>
<td>X</td>
</tr>
</tbody>
</table>
<p>首先我们来讲一下上面这张表格,当我们第一列的关键字进行声明时,我们在做什么。</p>
<pre><code class="language-js">namespace job {
haircut(): void;
}
class Man{
name: string;
}
let imgss = new Man();
enum Color {red, blue, yellow}
interface dogfood {
brand: string;
price: number
}
type event = 'mouse' | 'keyboard';
function foo(){}
let a = 2;
var b = {};
const c = null;
</code></pre>
<p>namespace 用来声明一个命名空间,比较著名的命名空间有lodash,里面有一堆工具函数,统统放在一个叫_的 namespace 里面,同时你也可以<code>let $ = _</code>;所以namespace也声明了一个值。</p>
<p>class 声明了一个值,也声明了一种类型。你可以把Man赋值给一个变量,所以class是一种值,也可以说imgss是一个Man(类型),此时Man承担了一种类型的角色。</p>
<p>enum声明了一个值,也声明了一种类型。我们说red是一种Color,Color在这里承担类型的角色,也可以把Color赋值给一个变量</p>
<p>interface声明了一种类型,但是你不能把dogfood赋值给某个变量,否则你会得到一个报错``dogfood' only refers to a type, but is being used as a value here`</p>
<p>其他function,let,var,const都在声明一个值,你 不能说xxx是一个a,或者xxx是一个foo,不能把值当成类型使用。</p>
<h3 id="2-interface和class">2. interface和class</h3>
<p>我们知道,不算symbol,js中有6种基本类型,number,string,boolean,null, undefined, object。但是只依靠这几种类型,来描述某个函数需要传什么样的参数,是远远不够的,这也是interface的使命--描述一个值(value)的形状(type)。</p>
<p>现在我们来看class,class首先也具有interface的能力,描述一个形状,或者说代表一种类型。此外class还提供了实现,也就是说可以被实例化;</p>
<p>所以class可以implements interface:</p>
<pre><code class="language-js">interface ManLike {
speak(): void;
leg: number;
hand: number;
}
class Human implements ManLike {
leg: number = 2;
hand: number = 2;
speak() {
console.log('i can speak');
}
}
</code></pre>
<p>而interface可以extends class,此时的class承担类型的角色</p>
<pre><code class="language-js">interface Chinese extends Human {
country: string;
}
</code></pre>
<p>那么interface能不能extends enum或者type alias呢,这两个兄弟也声明了type啊,答案是不行的,官方报错的信息:</p>
<pre><code>An interface can only extend an object type or intersection of object types with statically known members.
</code></pre>
<h3 id="interface-和-type-alias">interface 和 type alias</h3>
<p>在大多数情况下,interface可以实现的,type alias也可以。官方文档给出了两者的区别,这边翻译一下,忽略掉报错提示的区别,主要有以下两点:</p>
<ol>
<li>type alias 可以重命名原始类型,interface不可以:</li>
</ol>
<pre><code class="language-ts">type Num = number;
// interface 不行
</code></pre>
<ol start="2">
<li>interface可以进行declaration merging, type alias不可以:</li>
</ol>
<pre><code class="language-ts">interface Mammal {
genus: string
}
interface Mammal {
breed: string
}
// 前面两个interface被合并成一个同时具有genus和breed属性的类型
const animal: Mammal = {
genus: "1234",
// Fails because breed has to be a string
breed: '2'
}
type Reptile = {
genus: string
}
// type一旦被声明,不能再加新属性
type Reptile = {
breed?: string
}
</code></pre>
<p>这会导致一个有趣的问题,这个问题在github上有人提出:</p>
<pre><code class="language-ts">// -------- 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 {
// 需要增加索引签名
: string | number;
id: string;
}
</code></pre>
<p>原因就是interface可以多次声明,可以被declaretion merging,__IGetUserServiceList 加入索引签名之后,可以将interface后续加入的属性约束在一个范围内<code> : string | number</code>,保证__IGetUserServiceList符合Data的shape。</p>
<h3 id="3-class和abstract-class">3. class和abstract class</h3>
<p>class和abstract class的区别主要是abstract class不能被实例化:</p>
<pre><code class="language-js">abstract Human {
name: string;
abstract lang(): void;
toString() {
return `<human:${this.name}>`
}
}
new Human // Cannot create an instance of an abstract class.
</code></pre>
<h3 id="4-interface和abstract-class">4. interface和abstract class</h3>
<p>两者都不能被实例化,但是abstract class 也可以被赋值给变量。<br>
interface 里面不能有方法的实现,abstract class 可以提供部分的方法实现,这些方法可以被子类调用。</p>
<p>参考: https://www.typescriptlang.org/docs/handbook/declaration-merging.html</p>
</div>
<div id="MySignature" role="contentinfo">
<strong>cnblogs-md-editor编辑器</strong>,用Markdown写博客就用它<br><br>
来源:https://www.cnblogs.com/imgss/p/13173091.html
頁:
[1]