浅海深水 發表於 2023-3-20 15:06:00

TypeScript 学习笔记 — 类型兼容 (十)

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>一.基本数据类型的兼容性</li><li>二.接口兼容性</li><li>三.函数的兼容性</li><li>四.类的兼容性<ul><li>类的私有成员和受保护成员</li></ul></li><li>五.泛型的兼容性</li><li>六.枚举的兼容性</li><li>标称类型简短介绍</li></ul></div><br>
TS 是结构类型系统(structural type system),基于结构/形状检查类型,而非类型的名字。<p></p>
<p>TS 中的兼容性,主要看<strong><code>结构是否兼容</code></strong>。(核心是考虑安全性),结构化的类型系统(又称鸭子类型检查),如两个类型名字不一样但是无法区分<br>
类型兼容性是基于结构子类型的。 结构类型是一种只使用其成员来描述类型的方式。</p>
<blockquote>
<p>如果<code>x</code>要兼容<code>y</code>,那么<code>y</code>至少具有与<code>x</code>相同的属性。<br>
这里要检查<code>y</code>是否能赋值给<code>x</code>,编译器检查<code>x</code>中的每个属性,看是否能在<code>y</code>中也找到对应属性。<br>
X 兼容 Y:X(目标类型)= Y(源类型)<br>
简单一句话概括兼容性: 重新赋值不报错(类型自动转化)</p>
</blockquote>
<h2 id="一基本数据类型的兼容性">一.基本数据类型的兼容性</h2>
<pre><code class="language-ts">let temp: string | number;
let num!: number;
temp = num;
</code></pre>
<pre><code class="language-ts">let obj: {
toString(): string;
};
let str: string = "yya";
obj = str; // 字符串中具备toString()方法,所以可以进行兼容
obj.toString(); // 安全, 保证使用的时候不会发生异常
</code></pre>
<h2 id="二接口兼容性">二.接口兼容性</h2>
<p>接口的兼容性,只要满足接口中所需要的类型即可!(保证你要的,我都有,就行,多了也没关系)</p>
<pre><code class="language-ts">interface IAnimal {
name: string;
age: number;
}
interface IPerson {
name: string;
age: number;
address: string;
}
let animal: IAnimal;
let person: IPerson = {
name: "yya",
age: 18,
address: "beijing",
};

type T2 = IPerson extends IAnimal ? true : false; // true
animal = person; // 子类赋予给父类 兼容
</code></pre>
<h2 id="三函数的兼容性">三.函数的兼容性</h2>
<p>函数的兼容性主要是比较参数和返回值</p>
<p><strong>参数</strong>:赋值函数的参数要少于等于被赋值的函数:也就是说,对应函数的参数来讲,<code>少的参数可以赋予给多的</code>,因为内部实现传了多个可以少用或不用(忽略额外的参数在 JavaScript 里是很常见的)</p>
<p><code>sum2</code>的每个参数必须能在<code>sum1</code>里找到对应类型的参数。 注意的是参数的名字相同与否无所谓,<code>只看它们的类型</code>。 <code>sum2</code>的每个参数在<code>sum1</code>中都能找到对应的参数,所以允许赋值。</p>
<pre><code class="language-ts">let sum1 = (a: string, b: string) =&gt; a + b;
let sum2 = (a: string) =&gt; a;
sum1 = sum2;
</code></pre>
<p><mark>举例:</mark> <code>Array#forEach</code>给回调函数传 3 个参数:item,index 和 array。 尽管如此,传入一个只使用第一个参数的回调函数也是可以的</p>
<pre><code class="language-ts">type Func&lt;T&gt; = (item: T, index: number, array: any[]) =&gt; void;
function forEach&lt;T&gt;(arr: T[], cb: Func&lt;T&gt;) {
for (let i = 0; i &lt; arr.length; i++) {
    cb(arr, i, arr);
}
}
forEach(, (item) =&gt; {
console.log(item);
});
</code></pre>
<p><strong>返回值</strong>:</p>
<pre><code class="language-ts">type sum1 = () =&gt; string | number;
type sum2 = () =&gt; string;

let fn1: sum1;
let fn2!: sum2;
fn1 = fn2;
</code></pre>
<h2 id="四类的兼容性">四.类的兼容性</h2>
<p>类与对象字面量和接口差不多,但有一点不同:类有静态部分和实例部分的类型。 比较两个类类型的对象时,只有实例的成员会被比较。 静态成员和构造函数不在比较的范围内。</p>
<pre><code class="language-ts">class Animal {
feet!: number;
constructor(name: string, numFeet: number) {}
}

class Size {
feet!: number;
constructor(numFeet: number) {}
}

let a!: Animal;
let s!: Size;

a = s; // OK
s = a; // OK
</code></pre>
<h3 id="类的私有成员和受保护成员">类的私有成员和受保护成员</h3>
<p>只要有 private 或者 protected 关键字会影响兼容性, 当检查类实例的兼容时,如果目标类型包含一个 private 私有成员,那么源类型<code>必须包含来自同一个类的这个私有成员</code>。 这条规则也适用于包含 protected 受保护成员实例的类型检查。 允许子类赋值给父类,但是不能赋值给其它有同样类型的类。</p>
<pre><code class="language-ts">class A {
private name!: string;
age!: number;
}

class B {
private name!: string;
age!: number;
}

// let a: A = new B();// error

class Parent {
protected name: string = "zf";
age: number = 11;
}
class Child extends Parent {}
let child: Parent = new Child(); // ok
</code></pre>
<h2 id="五泛型的兼容性">五.泛型的兼容性</h2>
<p>泛型比较的是最终的结果 比较的不是泛型传递的参数<br>
<mark>例一:</mark></p>
<pre><code class="language-ts">interface Empty&lt;T&gt; {}

let x: Empty&lt;number&gt;;
let y!: Empty&lt;string&gt;;

type xx = Empty&lt;number&gt; extends Empty&lt;string&gt; ? true : false; // true

x = y; // OK 因为 y 匹配 x 的结构
</code></pre>
<p>在例一中,x 和 y 是兼容的,因为它们的结构使用类型参数时并没有什么不同。 把这个例子改变一下,增加一个成员,就能看出是如何工作的了:<br>
<mark>例二:</mark></p>
<pre><code class="language-ts">interface NotEmpty&lt;T&gt; {
data: T;
}
let x: NotEmpty&lt;number&gt;;
let y: NotEmpty&lt;string&gt;;

type xx = NotEmpty&lt;number&gt; extends NotEmpty&lt;string&gt; ? true : false; // false

x = y; // Error,不兼容
</code></pre>
<p>对于没指定泛型类型的泛型参数时,会把所有泛型参数当成 any 比较。 然后用结果类型进行比较,就像例一:</p>
<pre><code class="language-ts">let identity = function &lt;T&gt;(x: T): T {};

let reverse = function &lt;U&gt;(y: U): U {};

identity = reverse; // OK,(x: any) =&gt; any 匹配 (y: any) =&gt; any
</code></pre>
<h2 id="六枚举的兼容性">六.枚举的兼容性</h2>
<p>枚举类型与数字类型兼容,并且数字类型与枚举类型兼容</p>
<pre><code class="language-ts">enum Status {
Pending,
Resolved,
Rejected,
}

let current = Status.Pending;
let num = 0;

current = num;
num = current;
</code></pre>
<p>不同枚举类型之间是不兼容的。</p>
<pre><code class="language-ts">enum Status {
Pending,
Resolved,
Rejected,
}
enum Color {
Red,
Blue,
Green,
}

let current = Status.Pending;
let color = Color.Red;

current = color; // 不能将类型“Color.Red”分配给类型“Status”
</code></pre>
<h2 id="标称类型简短介绍">标称类型简短介绍</h2>
<p>类型分为两种 结构化类型(structural type system) 、标称类型(nominal type system)<br>
标称类型: 虽然 BTC,USDT 都是 number 类型,但还是想要用不同的类型表示,且不能互换,数据的值本身没什么区别,安上不同名字就是不同类型,也就是说,标称类型系统中,两个变量是否类型兼容(可以交换赋值)取决于这两个变量显式声明的类型名字是否相同。</p>
<pre><code class="language-ts">class AddType&lt;S&gt; {
private _type!: S;
}
type NewType&lt;T, S extends string&gt; = T &amp; AddType&lt;S&gt;;

type BTC = NewType&lt;number, "btc"&gt;; // number + BTC
type USDT = NewType&lt;number, "usdt"&gt;; // number + USDT
let btc = 100 as BTC;
let usdt = 100 as USDT;

function getCount(count: USDT) {
return count;
}
getCount(usdt);
</code></pre><br><br>
来源:https://www.cnblogs.com/echoyya/p/17189305.html
頁: [1]
查看完整版本: TypeScript 学习笔记 — 类型兼容 (十)