TypeScript: type alias 与 interface

爱⌒轻易说出口 提交于 2020-05-02 18:26:56

<table class="d-block"> <tbody class="d-block"> <tr class="d-block"> <td class="d-block comment-body markdown-body js-comment-body">

<p>官方文档中有关于两者对比的信息,隐藏在 TypeScript Handbook 中,见 <a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html" rel="nofollow"><code>Interfaces vs. Type Aliases</code></a> 部分。</p> <p>但因为这一部分很久没更新了,所以其中描述的内容不一定全对。</p> <p>比如,</p> <h3>区别点之一:Type Alias 不会创建新的类型,体现在错误信息上。</h3> <blockquote> <p>One difference is, that interfaces create a new name that is used everywhere. Type aliases don’t create a new name — for instance, error messages won’t use the alias name.</p> </blockquote> <p>不完全正确。直接通过 type 定义的初始类型,是会创建相应的类型名称的。</p> <p>什么意思呢。就是说,不是使用 <code>&amp;</code>, <code>|</code> 等操作符创建的 union type 及 intersection type。</p> <div class="highlight highlight-source-js"><pre>type Person <span class="pl-k">=</span> { name<span class="pl-k">:</span> string; age<span class="pl-k">:</span> number; };

<span class="pl-c"><span class="pl-c">//</span> [x] Property 'age' is missing in type '{ name: string; }' but required in type 'Person'.ts(2741)</span> <span class="pl-k">const</span> bob<span class="pl-k">:</span> <span class="pl-c1">Person</span> <span class="pl-k">=</span> { name<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>bob<span class="pl-pds">"</span></span> };</pre></div>

<p>注意这里错误信息使用的是类型 <code>Person</code> 而不是对应的 plain object 对象。</p> <h3>区别点之二:type alias 不能被 <code>extends</code> 和 <code>implements</code>。</h3> <p>实际上在扩展和实现上二者已经没有区别,甚至可以混用,比如让一个 class 同时实现 interface 和 type alias 定义的类型。</p> <div class="highlight highlight-source-js"><pre>type PointType <span class="pl-k">=</span> { x<span class="pl-k">:</span> number; y<span class="pl-k">:</span> number; };

<span class="pl-k">interface</span> PointInterface { a<span class="pl-k">:</span> number; b<span class="pl-k">:</span> number; }

<span class="pl-k">class</span> <span class="pl-en">Shape</span> <span class="pl-k">implements</span> PointType, PointInterface { <span class="pl-en">constructor</span>(<span class="pl-k">public</span> <span class="pl-smi">a</span> <span class="pl-k">=</span> <span class="pl-c1">1</span>, <span class="pl-k">public</span> <span class="pl-smi">b</span> <span class="pl-k">=</span> <span class="pl-c1">2</span>, <span class="pl-k">public</span> <span class="pl-smi">x</span> <span class="pl-k">=</span> <span class="pl-c1">3</span>, <span class="pl-k">public</span> <span class="pl-smi">y</span> <span class="pl-k">=</span> <span class="pl-c1">4</span>) {} }</pre></div>

<h3>区别点之三:type alias 不能扩展和实现其他类型</h3> <p>不完全正确。因为通过正交操作符(intersection type) <code>&amp;</code> 可以达到 <code>extends</code> 的目的。</p> <div class="highlight highlight-source-js"><pre><span class="pl-k">interface</span> Person { name<span class="pl-k">:</span> string; }

<span class="pl-k">interface</span> Job { title<span class="pl-k">:</span> string; }

type EmployeeType <span class="pl-k">=</span> Person <span class="pl-k">&</span> Job;

<span class="pl-k">class</span> <span class="pl-en">Employee</span> <span class="pl-k">implements</span> EmployeeType { <span class="pl-en">constructor</span>(<span class="pl-k">public</span> <span class="pl-smi">name</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>Nobody<span class="pl-pds">"</span></span>, <span class="pl-k">public</span> <span class="pl-smi">title</span> <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">"</span>Noone<span class="pl-pds">"</span></span>) {} }</pre></div>

<p>上面可以看到,两者大部分情况下不用过多区分。</p> <p>在使用了 union type 的时候,一些区别才开始显现。其实也算不得区别,因为只有 type alias 可通过 union type 定义。</p> <h3>当 type 包含 union type 时,该类型是不能被实现和扩展的。</h3> <div class="highlight highlight-source-js"><pre><span class="pl-k">interface</span> Triangle { area<span class="pl-k">:</span> number; }

<span class="pl-k">interface</span> Square { width<span class="pl-k">:</span> number; height<span class="pl-k">:</span> number; }

type ShapeType <span class="pl-k">=</span> Triangle <span class="pl-k">|</span> Square;

<span class="pl-c"><span class="pl-c">//</span> [x] An interface can only extend an object type or intersection of object types with statically known members.ts(2312)</span> <span class="pl-k">interface</span> MyShape <span class="pl-k">extends</span> ShapeType;

<span class="pl-k">class</span> <span class="pl-en">Shape</span> <span class="pl-k">implements</span> ShapeType{ <span class="pl-c"><span class="pl-c">//</span> [x] A class can only implement an object type or intersection of object types with statically known members.ts(2422)</span> }</pre></div>

<p>因为 union type 描述的是一个<strong>或者</strong>的状态,一个类不可能即是此类型也是另外种类型。interface 也不可能继承一个类型还不确定的类型。</p> <h3>类型合并</h3> <p>最明显的一点区别,是在进行类型合并(<a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html" rel="nofollow">Declaration Merging</a>)的时候,type alias 是不会被合并的,而同名的多个 interface 会合并成一个。</p> <div class="highlight highlight-source-js"><pre><span class="pl-k">interface</span> Person { name<span class="pl-k">:</span> string; } <span class="pl-k">interface</span> Person { age<span class="pl-k">:</span> number; }

<span class="pl-k">const</span> person<span class="pl-k">:</span> <span class="pl-c1">Person</span> <span class="pl-k">=</span> { name<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">"</span>Bob<span class="pl-pds">"</span></span>, age<span class="pl-k">:</span> <span class="pl-c1">9</span> };</pre></div>

<p>对于 type alias,存在同名时直接报错。</p> <div class="highlight highlight-source-js"><pre>type Person { <span class="pl-c"><span class="pl-c">//</span> [x] Duplicate identifier 'Person'.ts(2300)</span> name<span class="pl-k">:</span> string; } type Person { <span class="pl-c"><span class="pl-c">//</span> [x] Duplicate identifier 'Person'.ts(2300)</span> age<span class="pl-k">:</span> number; }</pre></div> <p>明白这点对于三方库的作者来说很重要。假如你写了个 npm 包,导出的是 type,则使用者无法通过简单定义同名类型来进行扩充。</p> <p>所以,写库的时候,尽量使用 interface。</p> <h3>结论</h3> <p>官方推荐用 interface,其他无法满足需求的情况下用 type alias。</p> <p>但其实,因为 union type 和 intersection type 是很常用的,所以避免不了大量使用 type alias 的场景,一些复杂类型也需要通过组装后形成 type alias 来使用。所以,如果想保持代码统一,可尽量选择使用 type alias。通过上面的对比,type alias 其实可函盖 interface 的大部分场景。</p> <p>对于 React 组件中 props 及 state,使用 type alias,这样能够保证使用组件的地方不能随意在上面添加属性。如果有自定义需求,可通过 HOC (Higher-Order Components)二次封装。</p> <p>编写三方库时使用 interface,其更加灵活自动的类型合并可应对未知的复杂使用场景。</p> <h3>相关资料</h3> <ul> <li><a href="https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c" rel="nofollow">Interface vs Type alias in TypeScript 2.7</a></li> <li><a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html" rel="nofollow">TypeScript Handbook</a></li> <li><a href="https://kendaleiv.com/typescript-constructor-assignment-public-and-private-keywords/" rel="nofollow">TypeScript Constructor Assignment: public and private Keywords</a></li> </ul> </td> </tr> </tbody> </table>

原文出处:https://www.cnblogs.com/Wayou/p/type_alias_vs_interface.html

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!