TypeScript 真香系列——接口篇
<div><h2 class="heading" data-id="heading-0">接口带来了什么好处</h2>
<h3 class="heading" data-id="heading-1">好处One —— 过去我们写 JavaScript</h3>
<p>JavaScript 中定义一个函数,用来获取一个用户的姓名和年龄的字符串:</p>
<div class="cnblogs_code">
<pre>const getUserInfo = <span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)">(user) {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> name: ${user.name}, age: ${user.age}
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-params"><span class="hljs-keyword"><span class="hljs-attr"> </span></span></span></span></span></span></code></pre>
<p>函数调用:</p>
<div class="cnblogs_code">
<pre>getUserInfo({name: "koala", age: 18})</pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-attr"><span class="hljs-string"><span class="hljs-attr"><span class="hljs-number"><span class="copy-code-btn"> </span></span></span></span></span></code></pre>
<p>这对于我们之前在写 JavaScript 的时候,再正常不过了,但是如果这个 <code>getUserInfo</code> 在多人开发过程中,如果它是个<code>公共函数</code>,多个开发者都会调用,如果不是每个人点进来看函数对应<code>注释</code>,可能会出现以下问题:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 错误的调用</span>
getUserInfo() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Uncaught TypeError: Cannot read property 'name' of undefined</span>
console.log(getUserInfo({name: "kaola"})) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> name: kaola, age: undefined</span>
getUserInfo({name: "kaola", height: 1.66}) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> name: koala, age: undefined</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-built_in"><span class="hljs-attr"><span class="hljs-string"><span class="hljs-comment"><span class="hljs-attr"><span class="hljs-string"><span class="hljs-attr"><span class="hljs-number"><span class="hljs-comment"> </span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>JavaScript 是<strong>弱类型</strong>的语言,所以并不会对我们传入的代码进行任何的检测,<strong>有些错你自己都说不清楚</strong>,但是就出了问题。</p>
<h4 class="heading" data-id="heading-2">TypeScript 中的 interface 可以解决这个问题</h4>
<div class="cnblogs_code">
<pre>const getUserInfo = (user: {name: string, age: number}): string =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> `name: ${user.name} age: ${user.age}`;
};</span></pre>
</div>
<p> </p>
<p>正确的调用是如下的方式:</p>
<div class="cnblogs_code">
<pre>getUserInfo({name: "kaola", age: 18});</pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-attr"><span class="hljs-string"><span class="hljs-attr"><span class="hljs-number"> </span></span></span></span></code></pre>
<p>如果调用者出现了错误的调用,那么 <code>TypeScript</code> 会直接给出错误的提示信息:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 错误的调用</span>
getUserInfo(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 错误信息:An argument for 'user' was not provided.</span>
getUserInfo({name: "coderwhy"}); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 错误信息:Property 'age' is missing in type '{ name: string; }'</span>
getUserInfo({name: "coderwhy", height: 1.88}); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 错误信息:类型不匹配</span></pre>
</div>
<pre><code class="hljs bash copyable" lang="bash"><span class="hljs-keyword"><span class="hljs-string"><span class="hljs-string"><span class="hljs-string"><span class="hljs-keyword"><span class="hljs-built_in"><span class="hljs-string"><span class="hljs-string"> </span></span></span></span></span></span></span></span></code></pre>
<p>这时候你会发现这段代码还是有点长,代码不便与阅读,这时候就体现了 <code>interface</code> 的必要性。</p>
<p><strong>使用 interface 对 user 的类型进行重构。</strong></p>
<p>我们先定义一个 <code>IUser</code> 接口:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 先定义一个接口</span>
<span style="color: rgba(0, 0, 0, 1)">interface IUser {
name: string;
age: number;
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-comment"><span class="hljs-attr"> </span></span></code></pre>
<p>接下来我们看一下<code>函数</code>如何来写:</p>
<div class="cnblogs_code">
<pre>const getUserInfo = (user: IUser): string =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> `name: ${user.name}, age: ${user.age}`;
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 正确的调用</span>
getUserInfo({name: "koala", age: 18});</pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-params"><span class="hljs-keyword"><span class="hljs-string"><span class="hljs-subst"><span class="hljs-subst"><span class="hljs-comment"><span class="hljs-attr"><span class="hljs-string"><span class="hljs-attr"><span class="hljs-number"> </span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>// 错误的调用和之前一样,报错信息也相同不再说明。</p>
<p><strong>接口中函数的定义再次改造</strong></p>
<p>定义两个接口:</p>
<div class="cnblogs_code">
<pre>type IUserInfoFunc = (user: IUser) =><span style="color: rgba(0, 0, 0, 1)"> string;
interface IUser {
name: string;
age: number;
}</span></pre>
</div>
<pre><code class="hljs typescript copyable" lang="typescript"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-params"><span class="hljs-built_in"><span class="hljs-keyword"><span class="hljs-built_in"><span class="hljs-built_in"> </span></span></span></span></span></span></span></code></pre>
<p>接着我们去定义函数和调用函数即可:</p>
<div class="cnblogs_code">
<pre>const getUserInfo: IUserInfoFunc = (user) =><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> `name: ${user.name}, age: ${user.age}`;
};</span></pre>
</div>
<pre><code class="hljs typescript copyable" lang="typescript"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-params"><span class="hljs-keyword"><span class="hljs-string"><span class="hljs-subst"><span class="hljs-subst"> </span></span></span></span></span></span></span></code></pre>
<p>// 正确的调用</p>
<div class="cnblogs_code">
<pre>getUserInfo({name: "koala", age: 18});</pre>
</div>
<pre><code class="hljs typescript copyable" lang="typescript"><span class="hljs-string"><span class="hljs-number"> </span></span></code></pre>
<p>// 错误的调用</p>
<div class="cnblogs_code">
<pre>getUserInfo();</pre>
</div>
<pre><code class="hljs typescript copyable" lang="typescript"> </code></pre>
<h3 class="heading" data-id="heading-3">好处TWO —— 过去我们用 Node.js 写后端接口</h3>
<p>其实这个说明和上面类似,我再提一下,就是想证明 TypeScript 确实挺香的! 写一个后端接口,我要特意封装一个工具类,来检测前端给我传递过来的参数,比如下图中的<code>validate</code>专门用来检验参数的函数</p>
<img class="lazyload inited loaded" src="https://user-gold-cdn.xitu.io/2019/11/18/16e7a1d724cdef1a?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="" data-src="https://user-gold-cdn.xitu.io/2019/11/18/16e7a1d724cdef1a?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" data-width="1280" data-height="371"> 但是有了 TypeScript 这个参数检验函数可以省略了,我们可以这样写:
<p> </p>
<div class="cnblogs_code">
<pre> const goodParams: IGoodsBody = <span style="color: rgba(0, 0, 255, 1)">this</span>.ctx.body;</pre>
</div>
<p> </p>
<p>而<code>GoodsBody</code>就是对应参数定义的 <code>interface</code>,比如这个样子</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> -- 查询列表时候使用的接口</span>
<span style="color: rgba(0, 0, 0, 1)">interface IQuery {
page: number;
rows: number;
disabledPage</span>?: <span style="color: rgba(0, 0, 255, 1)">boolean</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 是否禁用分页,true将会忽略`page`和`rows`参数</span>
<span style="color: rgba(0, 0, 0, 1)"> }
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> - 商品</span>
<span style="color: rgba(0, 0, 0, 1)">export interface IGoodsQuery extends Query {
isOnline</span>?: string | number; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 是否出售中的商品</span>
goodsNo?: string; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 商品编号</span>
goodsName?: string; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 商品名称</span>
}</pre>
</div>
<pre></pre>
<h2 class="heading" data-id="heading-4">接口的基础篇</h2>
<h3 class="heading" data-id="heading-5">接口的定义</h3>
<p>和 java 语言相同,TypeScript 中定义接口也是使用 interface 关键字来定义:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface IQuery {
page: number;
}</span></pre>
</div>
<pre><code class="hljs typescript copyable" lang="typescript"><span class="hljs-keyword"><span class="hljs-built_in"> </span></span></code></pre>
<p>你会发现我都在接口的前面加了一个<code>I</code>,算是个人习惯吧,之前一直写 java 代码,另一方面<code>tslint</code>要求,否则会报一个警告,是否加看个人。</p>
<h3 class="heading" data-id="heading-6">接口中定义方法</h3>
<p>看上面的接口中,我们定义了 <code>page</code> 常规属性,定义接口时候不仅仅可以有 属性,也可以有方法,看下面的例子:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface IQuery {
page: number;
findOne(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
findAll(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-attr"><span class="hljs-keyword"><span class="hljs-keyword"> </span></span></span></code></pre>
<p>如果我们有一个对象是该接口类型,那么必须包含对应的属性和方法(无可选属性情况):</p>
<div class="cnblogs_code">
<pre>const q: IQuery =<span style="color: rgba(0, 0, 0, 1)"> {
page: </span>1<span style="color: rgba(0, 0, 0, 1)">,
findOne() {
console.log(</span>"findOne"<span style="color: rgba(0, 0, 0, 1)">);
},
findAll() {
console.log(</span>"findAll"<span style="color: rgba(0, 0, 0, 1)">);
},
};</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-keyword"><span class="hljs-attr"><span class="hljs-number"><span class="hljs-built_in"><span class="hljs-string"><span class="hljs-built_in"><span class="hljs-string"> </span></span></span></span></span></span></span></code></pre>
<h3 class="heading" data-id="heading-7">接口中定义属性</h3>
<h4 class="heading" data-id="heading-8">普通属性</h4>
<p>上面的 <code>page</code> 就是普通属性,如果有一个对象是该接口类型,那么必须包含对应的普通属性。就不具体说了。</p>
<h4 class="heading" data-id="heading-9">可选属性</h4>
<p>默认情况下一个变量(对象)是对应的接口类型,那么这个变量(对象)必须实现接口中所有的属性和方法。</p>
<p>但是,开发中为了让接口更加的灵活,某些属性我们可能希望设计成可选的(想实现可以实现,不想实现也没有关系),这个时候就可以使用<code>可选属性</code>(后面详细讲解函数时,也会讲到函数中有可选参数):</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface IQuery {
page: number;
findOne(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
findAll(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
isOnline</span>?: string | number; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 是否出售中的商品</span>
<span style="color: rgba(0, 0, 255, 1)">delete</span>?(): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-attr"><span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-comment"><span class="hljs-keyword"><span class="hljs-keyword"> </span></span></span></span></span></span></code></pre>
<p>上面的代码中,我们增加了<code>isOnline</code>属性和<code>delete</code>方法,这两个都是可选的:</p>
<blockquote>
<p>注意:可选属性如果没有赋值,那么获取到的值是<code>undefined</code>; 对于可选方法,必须先进行判断,再调用,否则会报错;</p>
</blockquote>
<div class="cnblogs_code">
<pre>const q: IQuery =<span style="color: rgba(0, 0, 0, 1)"> {
page: </span>1<span style="color: rgba(0, 0, 0, 1)">,
findOne() {
console.log(</span>"findOne"<span style="color: rgba(0, 0, 0, 1)">);
},
findAll() {
console.log(</span>"findAll"<span style="color: rgba(0, 0, 0, 1)">);
},
};
console.log(p.isOnline); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> undefined</span>
p.<span style="color: rgba(0, 0, 255, 1)">delete</span>(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 不能调用可能是“未定义”的对象。</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-keyword"><span class="hljs-attr"><span class="hljs-number"><span class="hljs-built_in"><span class="hljs-string"><span class="hljs-built_in"><span class="hljs-string"><span class="hljs-built_in"><span class="hljs-comment"><span class="hljs-comment"> </span></span></span></span></span></span></span></span></span></span></code></pre>
<p>正确的调用方式如下:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">if</span> (p.<span style="color: rgba(0, 0, 255, 1)">delete</span><span style="color: rgba(0, 0, 0, 1)">) {
p.</span><span style="color: rgba(0, 0, 255, 1)">delete</span><span style="color: rgba(0, 0, 0, 1)">();
}</span></pre>
</div>
<p> </p>
<p>大家可能会问既然是可选属性,可有可无的,那么为什么还要定义呢?对比起完全不定义,定义可选属性主要是:为了让<code>接口更加的灵活</code>,某些属性我们可能希望设计成可选,并且如果存在属性,能<code>约束类型</code>,而这也是十分关键的。</p>
<h4 class="heading" data-id="heading-10">只读属性</h4>
<p>默认情况下,接口中定义的属性可读可写: 但是有一个关键字 <code>readonly</code>,定义的属性值,不可以进行修改,强制修改后报错。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface IQuery {
readonly page: number;
findOne(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-keyword"> </span></code></pre>
<p>给<code>page</code>属性加了<code>readonly</code>关键字,再给它赋值会报错。</p>
<div class="cnblogs_code">
<pre>const q: IQuery =<span style="color: rgba(0, 0, 0, 1)"> {
page: </span>1<span style="color: rgba(0, 0, 0, 1)">,
findOne() {
console.log(</span>"findOne"<span style="color: rgba(0, 0, 0, 1)">);
},
};
q.page </span>= 10;<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Cannot assign to 'page' because it is a read-only property.</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-keyword"><span class="hljs-attr"><span class="hljs-number"><span class="hljs-built_in"><span class="hljs-string"><span class="hljs-number"><span class="hljs-comment"> </span></span></span></span></span></span></span></code></pre>
<h2 class="heading" data-id="heading-11">接口的高级篇</h2>
<h3 class="heading" data-id="heading-12">函数类型接口</h3>
<p>Interface 还可以用来规范函数的形状。Interface 里面需要列出参数列表返回值类型的函数定义。写法如下:</p>
<ul>
<li>定义了一个函数接口</li>
<li>接口接收三个参数并且不返回任何值</li>
<li>使用函数表达式来定义这种形状的函数</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface Func {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ✔️ 定于这个函数接收两个必选参数都是 number 类型,以及一个可选的字符串参数 desc,这个函数不返回任何值</span>
(x: number, y: number, desc?: string): <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">
}
const sum: Func </span>= <span style="color: rgba(0, 0, 255, 1)">function</span> (x, y, desc = ''<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> const sum: Func = function (x: number, y: number, desc: string): void {</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ts类型系统默认推论可以不必书写上述类型定义</span>
console.log(desc, x +<span style="color: rgba(0, 0, 0, 1)"> y)
}
sum(</span>32, 22)</pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-comment"><span class="hljs-attr"><span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-function"><span class="hljs-keyword"><span class="hljs-params"><span class="hljs-string"><span class="hljs-comment"><span class="hljs-comment"><span class="hljs-built_in"><span class="hljs-number"><span class="hljs-number"> </span></span></span></span></span></span></span></span></span></span></span></span></span></code></pre>
<p>注意:不过上面的接口中只有一个函数,TypeScript 会给我们一个建议,可以使用 <code>type</code> 来定义一个函数的类型:</p>
<div class="cnblogs_code">
<pre>type Func = (x: number, y: number, desc?: string) => <span style="color: rgba(0, 0, 255, 1)">void</span>;</pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-function"><span class="hljs-params"><span class="hljs-keyword"> </span></span></span></code></pre>
<h3 class="heading" data-id="heading-13">接口的实现</h3>
<p>接口除了定义某种<code>类型规范</code>,也可以和其他编程语言一样,让一个<code>类去实现某个接口</code>,那么这个类就必须明确去拥有这个接口中的属性和实现其方法:</p>
<p>下面的代码中会有关于修饰符的警告,暂时忽略,后面详细讲解 // 定义一个实体接口</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface Entity {
title: string;
log(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-attr"><span class="hljs-keyword"> </span></span></code></pre>
<p>// 实现这样一个接口</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">class Post implements Entity {
title: string;
constructor(title: string) {
</span><span style="color: rgba(0, 0, 255, 1)">this</span>.title =<span style="color: rgba(0, 0, 0, 1)"> title;
}
log(): </span><span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> {
console.log(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">.title);
}
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-class"><span class="hljs-keyword"><span class="hljs-title"><span class="hljs-title"><span class="hljs-title"><span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-keyword"><span class="hljs-built_in"><span class="hljs-keyword"> </span></span></span></span></span></span></span></span></span></span></code></pre>
<p><strong>有些小伙伴的疑问?我定义了一个接口,但是我在继承这个接口的类中还要写接口的实现方法,那我不如直接就在这个类中写实现方法岂不是更便捷,还省去了定义接口?这是一个初学者经常会有疑惑的地方。</strong></p>
<p>解答这个疑惑之前,先记住两个字,<strong>规范</strong>!</p>
<p>这个规范可以达到你一看这名字,就知道他是用来干什么的,并且可拓展,可以维护。</p>
<ul>
<li>
<p>在<strong>代码设计</strong>中,接口是一种规范; 接口通常用于来定义某种规范, 类似于你必须遵守的协议,</p>
</li>
<li>
<p>站在<strong>程序角度</strong>上说接口只规定了类里必须提供的属性和方法,从而分离了规范和实现,增强了系统的可拓展性和可维护性;</p>
</li>
</ul>
<h3 class="heading" data-id="heading-14">接口的继承</h3>
<p>和类一样,接口也能继承其他的接口。这相当于复制接口的所有成员。接口也是用关键字 <code>extends</code> 来继承。</p>
<div class="cnblogs_code">
<pre>interface Shape { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">定义接口Shape</span>
<span style="color: rgba(0, 0, 0, 1)"> color: string;
}
interface Square extends Shape {</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">继承接口Shape</span>
<span style="color: rgba(0, 0, 0, 1)"> sideLength: number;
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-comment"><span class="hljs-comment"> </span></span></code></pre>
<p>一个 interface 可以同时继承多个 interface ,实现多个接口成员的合并。用<strong>逗号</strong>隔开要继承的接口。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-attr"><span class="hljs-attr"><span class="hljs-attr"> </span></span></span></code></pre>
<p> 需要注意的是,尽管支持继承多个接口,但是如果继承的接口中,定义的同名属性的类型不同的话,是<strong>不能编译通过</strong>的。如下代码:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface Shape {
color: string;
test: number;
}
interface PenStroke extends Shape{
penWidth: number;
test: string;
}</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-attr"><span class="hljs-attr"> </span></span></code></pre>
<p>另外关于继承还有一点,如果现在有一个类实现了 Square 接口,那么不仅仅需要实现 Square 的方法,也需要实现 Square 继承自的接口中的方法,实现接口使用 <code>implements</code> 关键字 。</p>
<h3 class="heading" data-id="heading-15">可索引类型接口</h3>
<h3 class="heading" data-id="heading-16">interface和type的区别</h3>
<h4 class="heading" data-id="heading-17">type 可以而 interface 不行</h4>
<ul>
<li>type 可以声明基本类型别名,联合类型,元组等类型</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 基本类型别名</span>
type Name =<span style="color: rgba(0, 0, 0, 1)"> string
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 联合类型</span>
<span style="color: rgba(0, 0, 0, 1)">interface Dog {
wong();
}
interface Cat {
miao();
}
type Pet </span>= Dog |<span style="color: rgba(0, 0, 0, 1)"> Cat
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 具体定义数组每个位置的类型</span>
type PetList = </pre>
</div>
<p> </p>
<ul>
<li>type 语句中还可以使用 typeof 获取实例的 类型进行赋值</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 当你想获取一个变量的类型时,使用 typeof</span>
<span style="color: rgba(0, 0, 0, 1)">
let div </span>= document.createElement('div'<span style="color: rgba(0, 0, 0, 1)">);
type B </span>= <span style="color: rgba(0, 0, 255, 1)">typeof</span> div</pre>
</div>
<ul>
<li>type 其他骚操作</li>
</ul>
<div class="cnblogs_code">
<pre>type StringOrNumber = string |<span style="color: rgba(0, 0, 0, 1)"> number;
type Text </span>= string |<span style="color: rgba(0, 0, 0, 1)"> { text: string };
type NameLookup </span>= Dictionary<string, Person><span style="color: rgba(0, 0, 0, 1)">;
type Callback</span><T> = (data: T) => <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)">;
type Pair</span><T> =<span style="color: rgba(0, 0, 0, 1)"> ;
type Coordinates </span>= Pair<number><span style="color: rgba(0, 0, 0, 1)">;
type Tree</span><T> = T | { left: Tree<T>, right: Tree<T> };</pre>
</div>
<h4 class="heading" data-id="heading-18">interface 可以而 type 不行</h4>
<p>interface 能够声明合并</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">interface User {
name: string
age: number
}
interface User {
sex: string
}
</span><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
User 接口为 {
name: string
age: number
sex: string
}
</span><span style="color: rgba(0, 128, 0, 1)">*/</span></pre>
</div>
<pre><code class="hljs javascript copyable" lang="javascript"><span class="hljs-attr"><span class="hljs-attr"><span class="hljs-comment"> </span></span></span></code></pre>
<p>另外关于type的更多内容,可以查看文档:TypeScript官方文档</p>
<h2 class="heading" data-id="heading-19">接口的应用场景总结</h2>
<p>在项目中究竟怎么用,开篇已经举了两个例子,在这里再简单写一点,最近尝试了一下egg+ts,学习下。在写查询参数检验的时候,或者返回固定数据的时候,都会用到接口,看一段简单代码,已经看完了上面的文章,自己体会下吧。</p>
<div class="cnblogs_code">
<pre>import User from '../model/user'<span style="color: rgba(0, 0, 0, 1)">;
import Good from </span>'../model/good'<span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义基本查询类型</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)"> -- 查询列表时候使用的接口</span>
<span style="color: rgba(0, 0, 0, 1)">interface Query {
page: number;
rows: number;
disabledPage</span>?: <span style="color: rgba(0, 0, 255, 1)">boolean</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 是否禁用分页,true将会忽略`page`和`rows`参数</span>
<span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 定义基本返回类型</span>
type GoodResult<Entity> =<span style="color: rgba(0, 0, 0, 1)"> {
list: Entity[];
total: number;
: any;
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> - 商品</span>
<span style="color: rgba(0, 0, 0, 1)">export interface GoodsQuery extends Query {
isOnline</span>?: string | number; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 是否出售中的商品</span>
goodsNo?: string; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 商品编号</span>
goodsName?: string; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 商品名称</span>
<span style="color: rgba(0, 0, 0, 1)">}
export type GoodResult </span>= QueryResult<Good>;</pre>
</div>
</div>
<p><br>作者:ikoala<br>链接:https://juejin.im/post/5dd1098e51882529f21587db<br>来源:掘金<br>著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。</p><p>喜欢这篇文章?欢迎打赏~~</p><p><img src="https://img2020.cnblogs.com/blog/1313648/202012/1313648-20201207210415386-746901846.png" alt="" loading="lazy"></p><p> </p><br><br>
来源:https://www.cnblogs.com/cangqinglang/p/11887535.html
頁:
[1]