為什么 TypeScript
Vue2.x 對(duì) TypeScript 的支持是硬傷旬陡,而 TypeScript 對(duì)于大型項(xiàng)目更加友好驶睦,團(tuán)隊(duì)協(xié)作時(shí)強(qiáng)類型總比約定更嚴(yán)謹(jǐn)廉羔。所以孩饼,一些大型工程在技術(shù)選型時(shí)此迅,拋棄 Vue 而選擇 React。尤其是在 React16.8+ 函數(shù)式組件有狀態(tài)以后。
為什么會(huì)出現(xiàn) setup
首先嘁酿,Vue 只是 MVC 中的 View 層,需要將 Model、Controller 分離開才能最大化地降低耦合、增加復(fù)用铐刘。即Vue 單文件組件應(yīng)該只是處理視圖層的業(yè)務(wù)邏輯及渲染即可俯艰,而 Controller 的業(yè)務(wù)邏輯不應(yīng)該在單文件組件中画株,因此誕生了 Vuex芹关,而 Vuex 更多是一個(gè)全局狀態(tài)管理诗祸,在處理一些業(yè)務(wù)邏輯怀樟、數(shù)據(jù)交互共耍、組件通信上,使用 Vuex 來管理顯得太重了。因而在 View 與 Controller 的業(yè)務(wù)邏輯處理上,有了一個(gè)真空地帶洲脂,導(dǎo)致我們?cè)?methods 中耦合了大量的業(yè)務(wù)邏輯疆液,隨著版本不斷迭代潘飘,一旦體量變大,公共方法的抽離掉缺、代碼維護(hù)的成本指數(shù)倍增長(zhǎng)卜录,組件已變得不可復(fù)用眶明,難以閱讀艰毒,能做的只能繼續(xù)往上面堆;
其次搜囱,一些需要響應(yīng)式的公共屬性丑瞧、方法,我們通常引入 mixin 來隔離犬辰,但是 mixin 不太好管理嗦篱,容易污染。
不難發(fā)現(xiàn)幌缝,業(yè)務(wù)邏輯耦合度變高后,最終導(dǎo)致 Vue 單文件組件的作為 View 層诫欠,頁(yè)面 UI 哪怕相似度極高涵卵,也可能無法復(fù)用浴栽。
高內(nèi)聚、低耦合轿偎,對(duì)于 Vue 來說典鸡,即需要能夠分離業(yè)務(wù)邏輯關(guān)注點(diǎn)、易管理坏晦、響應(yīng)式的API萝玷,setup 為了這而產(chǎn)生。
Vue 實(shí)踐
Props 自身的 type 屬性無法描述更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)昆婿,TS 會(huì)報(bào)錯(cuò)
Vue 提供了類型別名 PropType球碉,用于定義接收的更復(fù)雜的 props 屬性結(jié)構(gòu)。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n16" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">import { PropType, defineComponent, RendererElement } from 'vue';
interface ITabItem {
text: string,
id: string | number,
active?: boolean
}
const XComponent = defineComponent({
props: {
tabList: {
type: Array as PropType<Array<ITabItem>>,
default: () => []
}
},
setup() {
return (): RendererElement => (
<div>{
props.tabList.map(tab => {
return <p key={tab.id}>{tab.text}</p>
})
}</div>
)
}
})</pre>
單文件 vue 組件與 tsx 函數(shù)組件間的 slot 使用
單文件 vue 組件互相之間的 slot 使用仓蛆,可閱讀官方文檔 單文件插槽
jsx/tsx 組件互相之間的 slot 使用睁冬,可以閱讀文檔 jsx/tsx 插槽
單文件同 jsx、tsx 組件之間的 slot 使用看疙,示例如下:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="tsx" cid="n22" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">// vue 單文件中引入 tsx 函數(shù)式組件 XInput
<section>
<x-input v-model="inputInfo.value">
<template v-slot:search>
<p>This is search slot.</p>
</templatet>
</x-input>
</section>
// XInput
const SearchBox = (props: IChildProp) => {
const value = props.value;
const clickEv = () => {
// todo
props.clickHandler?.();
}
return (
<div class="search-box" onClick={clickEv}>
<img src={searchIcon} />
</div>
)
}
export default defineComponent({
setup(props, { emit, slots }) {
const value = ref(props.value);
const inputEv = (ev: Event) => {
value.value = (ev.target as HTMLInputElement).value;
emit('update:input', value);
}
const searchEv = ():void => {
// todo
}
return (): RendererElement => (
<div class="x-input">
<input class="input-el" value={value.value} onInput={inputEv} />
{
slots.search &&
(<>
<SearchBox clickHandler={searchEv} value={value.value} />
{slot.search?.()} // 渲染成 <p>This is a search slot.</p>
</>)
}
</div>
)
}
})</pre>
自定義hooks——將“動(dòng)作”轉(zhuǎn)成 “狀態(tài)”
// todo
巧用類型收縮解決 TypeScript 報(bào)錯(cuò)
1豆拨、類型斷言
類型斷言可以明確告訴 TypeScript 值的詳細(xì)類型,在某些場(chǎng)景能庆,我們非常確認(rèn)它的類型施禾,即使與 typescript 推斷出來的類型不一致。
2搁胆、類型守衛(wèi)
- typeof: 用于判斷 number, string, boolean 或 symbol 四種類型
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n36" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function padLeft(value: string | number) {
if (typeof value === string) {
console.log(value.length);
}
}</pre>
-
instanceof: 用于判斷一個(gè)實(shí)例是否屬于某個(gè)類
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n40" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">class Man {
handsome = 'handsome'
}
class Woman {
beautiful = 'beautiful'
}
function Human(arg: Man | Woman) {
if (arg instanceof Man) {
console.log(arg.handsome);
} else {
console.log(arg.beautiful);
}
}</pre> -
in: 用于判斷一個(gè)屬性/方法是否屬于某個(gè)對(duì)象
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n43" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">interface B {
b: string
}
interface A {
a: string
}
function foo(x: A | B) {
if ('a' in x) {
return x.a;
}
return x.b;
}</pre> -
字面量類型保護(hù)
有些場(chǎng)景拾积,使用 in, instanceof, typeof 太過麻煩。這時(shí)候可以自己構(gòu)造一個(gè)字面量類型
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n48" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">type Man = {
handsome: 'handsome';
type: 'man';
}
type Woman = {
beautiful: 'beautiful';
type: 'woman';
}
function Human(arg: Man | Woman) {
if (arg.type === 'man') {
console.log(arg.handsome);
} else {
console.log(arg.beautiful);
}
}</pre>
3丰涉、雙重?cái)嘌?/h3>
有些時(shí)候使用 as 也會(huì)報(bào)錯(cuò)拓巧,因?yàn)?as 斷言的時(shí)候也不是毫無條件的。它只有當(dāng) S 類型是 T 類型的子集時(shí)一死,S能被斷言成T肛度。
有些時(shí)候使用 as 也會(huì)報(bào)錯(cuò)拓巧,因?yàn)?as 斷言的時(shí)候也不是毫無條件的。它只有當(dāng) S 類型是 T 類型的子集時(shí)一死,S能被斷言成T肛度。
所以面對(duì)這樣的情況,只想暴力解決問題投慈,可以使用雙重?cái)嘌猿泄ⅲ紫葦嘌猿杉嫒菟蓄愋偷?unknown。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n54" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function handler(event: Event) {
const element = event as HTMLElement; // Error
// 'Event' 和 'HTMLElement' 中的任何一個(gè)都不能復(fù)制給另一個(gè)
}
// 首先斷言成 unknown
function handler(event: Event) {
const element = (event as unknown) as HTMLElement;
}</pre>
4伪煤、用戶自定義的類型保護(hù)
假若我們一旦檢查過類型加袋,就能在之后的每個(gè)分支里清楚地知道它的類型就好了。 TypeScript 里的類型保護(hù)
機(jī)制使其成為現(xiàn)實(shí)抱既。
類型保護(hù)就是一些表達(dá)式职烧,它們會(huì)在運(yùn)行時(shí)檢查以確保在某個(gè)作用域里的類型。要定義一個(gè)類型保護(hù),我們只要簡(jiǎn)單地定義一個(gè)函數(shù)蚀之,它的返回值是一個(gè)
類型謂詞
:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n60" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}</pre>
在這個(gè)例子里蝗敢,pet is Fish 就是類型謂詞。謂詞為 parameterName is Type
這種形式足删,parameterName
必須是來自當(dāng)前函數(shù)簽名里的一個(gè)參數(shù)名寿谴。
當(dāng)使用一些變量調(diào)用 isFish
時(shí),TypeScript 會(huì)將變量縮減為那個(gè)具體的類型失受,只要這個(gè)類型與變量的原始類型是兼容的讶泰。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n63" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">// TypeScript 不僅知道在 if 分支里 pet 是 Fish 類型;還清楚在 else 分支里拂到,一定不是 Fish 類型痪署,一定是 Bird 類型。
if (isFish(pet)) {
pet.swim();
} else {
pet.fly();
}</pre>
5谆焊、使用 never 收窄類型
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n66" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">interface Foo {
type: 'foo'
}
interface Bar {
type: 'bar'
}
// 一個(gè)聯(lián)合類型 All
type All = Foo | Bar;
function handleValue(val: All) {
switch (val.type) {
case 'foo':
break;
case 'bar':
break;
default:
// val 在這里是 never
const exhaustiveCheck: never = val;
break;
}
}</pre>
注意在 default 里面我們把收窄為 never 的 val 賦值給了一個(gè)現(xiàn)實(shí)聲明為 never 的變量惠桃。如果一切邏輯正常,那么這里應(yīng)該能夠編譯通過辖试。但是假如后來有一天你的同事改了 All 的類型:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n69" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">type All = Foo | Bar | Baz</pre>
然而他忘記了在 handleValue 里面加上針對(duì) Baz 的處理邏輯辜王,這個(gè)時(shí)候 default 里面 val 會(huì)被收窄為 Baz,導(dǎo)致無法復(fù)制給 never罐孝,產(chǎn)生一個(gè)編譯錯(cuò)誤呐馆。所以通過這個(gè)辦法,可以確保 handleValue 總是窮盡了所有的 All 的可能類型莲兢。
6汹来、非空斷言運(yùn)算符!
這個(gè)運(yùn)算符可以用在變量名或者函數(shù)名之后改艇,用來強(qiáng)調(diào)對(duì)應(yīng)的元素是 非 null|undefined 的收班,應(yīng)用場(chǎng)景,特別適合我們已經(jīng)明確知道不會(huì)返回空值的場(chǎng)景谒兄,從而減少冗余的代碼判斷摔桦。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n75" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">this.$refs.container!.scrollWidth</pre>
7、鍵值獲取 keyof
keyof 可以獲取一個(gè)類型所有鍵值承疲,返回一個(gè)聯(lián)合類型
:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n79" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">type Person = {
name: string;
age: number;
}
type PersonKey = keyof Person; // 'name' | 'age'</pre>
keyof 的一個(gè)典型應(yīng)用場(chǎng)景是限制訪問對(duì)象的 key 合法化邻耕,因?yàn)?any 做索引是不被接收的
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n81" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function getValue (p: Person, k: keyof Person) {
return p[k];
}</pre>
巧用高級(jí)類型靈活處理數(shù)據(jù)
1、類型索引
使用索引類型燕鸽,編譯器就能夠檢查使用了動(dòng)態(tài)屬性名的代碼兄世。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n89" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person {
name: 'Jack',
age: 18
}
let strings: string[] = pluck(person, ['name']);
</pre>
編譯器會(huì)檢查 name
是否真的是 Person
的一個(gè)屬性。
keyof T
索引類型查詢操作符啊研。對(duì)于任何類型 T御滩,keyof T 的結(jié)果為 T 上已知的公共屬性名的聯(lián)合鸥拧。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n93" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">let personProps: keyof Person; // 'name' | 'age';</pre>
T[K]
索引訪問操作符。在這里艾恼,類型語(yǔ)法反映了表達(dá)式語(yǔ)法住涉。就像索引類型查詢符一樣麸锉,可以在普通的上下文中使用T[K]
钠绍,只要確保類型變量K extends keyof T
就可以了。
keyof: 獲取類型上的 key 值
extends: 泛型里面的約束
-
T[P]: 獲取對(duì)象 T 相應(yīng) K 的元素類型
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n103" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">type Partial<T> = {
[P in keyof T]?: T[P]
}
// [自定義變量名 in 枚舉類型]: 類型</pre>
在使用 props 的時(shí)候花沉,有時(shí)候全部屬性都是可選的柳爽,如果一個(gè)一個(gè)屬性寫 ?碱屁,大量的重復(fù)動(dòng)作磷脯,這種時(shí)候可以直接使用 Partial<State>
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n108" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">// 如何用 ts 聲明 AnimalMap?
const AnimalMap = {
cat: { name: '貓', title: 'cat' },
dog: { name: '狗', title: 'dog' },
frog: { name: '蛙', title: 'wa' }
}
// Record 第一個(gè)泛型傳入對(duì)象的 key 值娩脾,第二個(gè)傳入對(duì)象的屬性
type Record<K extends string, T> = {
[P in K]: T;
}
type AnimalType = 'cat' | 'dog' | 'frog';
interface AnimalDescription {
name: string;
title: string
}
const AnimalMap: Record<AnimalType, AnimalDescription> {
cat: { name: '貓', title: 'cat' },
dog: { name: '狗', title: 'dog' },
frog: { name: '蛙', title: 'wa' }
}</pre>
2赵誓、可辨識(shí)聯(lián)合
合并單例類型,聯(lián)合類型柿赊,類型保護(hù)和類型別名來創(chuàng)建一個(gè)叫做
可辨識(shí)聯(lián)合
的高級(jí)模式俩功,它也稱作標(biāo)簽聯(lián)合
或代數(shù)數(shù)據(jù)
數(shù)據(jù)類型∨錾可辨識(shí)聯(lián)合在函數(shù)式編程中很有用處诡蜓。
三要素:
具有普通的單利類型屬性 —— 可辨識(shí)的特征
一個(gè)類型別名包含了那些類型的聯(lián)合 —— 聯(lián)合
此屬性上的類型保護(hù)
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n121" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">interface Square {
kind: 'square';
size: number
}
interface Rectangle {
kind: 'rectangle';
width: number;
height: number;
}
interface Circle {
kind: 'circle';
radius: number;
}
type Shape = Square | Rectangle | Circle;
function assertNever(x: never): never {
throw new Error("Unexpected object: " + x);
}
// 可辨識(shí)聯(lián)合
function area(s: Shape) {
switch (s.kind) {
case 'square':
return s.size * s.size;
case 'rectangle':
return s.height * s.width;
case 'circle':
return Math.PI * s.radius ** 2;
default:
return assertNever(s); // 處理遺漏的 case
}
}</pre>
assertNever
檢查 s
是否為 never
類型 —— 即為出去所有可能情況后剩下的類型。
裝飾器(Decorator)的理解
裝飾器(Decorator) 是一種特殊類型的聲明胰挑,它能夠被附加到
類聲明
蔓罚,方法
,訪問符
瞻颂,屬性
或參數(shù)
上豺谈。裝飾器使用@expression
這種形式,expression
求值后必須為一個(gè)函數(shù)贡这,它會(huì)在運(yùn)行時(shí)被調(diào)用茬末,被裝飾聲明信息作為參數(shù)傳入,它添加額外的方法或?qū)傩缘交惿吓号鳌槲覀冊(cè)陬惖穆暶骷俺蓡T上通過元編程語(yǔ)法添加標(biāo)注提供了一種方式团南。
我們簡(jiǎn)單的理解裝飾器,可以認(rèn)為它是一種包裝炼彪,對(duì)對(duì)象吐根、方法、屬性的包裝辐马。當(dāng)我們需要訪問一個(gè)對(duì)象的時(shí)候拷橘,如果我們通過這個(gè)對(duì)象外圍的包裝去訪問的話,被這個(gè)包裝附加的行為就會(huì)被觸發(fā)。例如一把加了消聲器的槍冗疮,消聲器就是一個(gè)裝飾器萄唇,但是它和原來的槍成為一個(gè)整體,開槍的時(shí)候消聲器就會(huì)發(fā)生作用术幔。
裝飾器組合
多個(gè)裝飾器可以同時(shí)應(yīng)用到一個(gè)聲明上:
-
書寫在同一行上:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n136" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">@f @g x</pre>
-
書寫在多行上(常用):
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n139" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">@f
@g
x</pre>
當(dāng)多個(gè)裝飾器應(yīng)用于一個(gè)聲明上另萤,它們求值方式與
復(fù)合函數(shù)
相似。
當(dāng)多個(gè)裝飾器應(yīng)用在一個(gè)聲明上時(shí)會(huì)進(jìn)行如下步驟的操作(調(diào)用棧诅挑,后進(jìn)先出):
1四敞、由上至下依次對(duì)裝飾器表達(dá)式求值
2、求值的結(jié)果會(huì)被當(dāng)作函數(shù)拔妥,由下至上依次調(diào)用
leDecorator是welcome函數(shù)的裝飾器:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n147" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function leDecorator(target, propertyKey: string, descriptor: PropertyDescriptor): any {
console.log('enter decorator');
const oldValue = descriptor.value;
descriptor.value = function() {
console.log(Calling "${propertyKey} with",
arguments, target, target instanceof Object ,descriptor);
const value = oldValue.apply(null, [arguments[1], arguments[0]]);
console.log('Function is executed');
return ${value}; This is awesome
;
};
return descriptor;
}
class JSMeetup {
public speaker = 'Ruban';
// @leDecorator
welcome(arg1, arg2) {
console.log(Arguments Received are ${arg1} ${arg2}
);
return ${arg1} ${arg2}
;
}
}
const meetup = new JSMeetup();
console.log(meetup.welcome('World', 'Hello'));
// 注釋掉 @leDecorator
/*
Arguments Received are World Hello
World Hello
*/
// 放開注釋
/*
enter decorator
Calling "welcome" with [Arguments] { '0': 'World', '1': 'Hello' } {}, true, {
value: [Function (anonymous)],
writable: true,
enumerable: false,
configuarble: true
}
Arguments Received are Hello World
Function is executed
Hello World; This is awesome
*/ </pre>
TypeScript 中共計(jì)有 5 類裝飾器:
方法裝飾器
屬性裝飾器
類裝飾器
參數(shù)裝飾器
訪問器裝飾器
方法裝飾器
方法裝飾器聲明在一個(gè)方法的聲明之前(緊靠著方法聲明)忿危。它會(huì)被應(yīng)用到方法的
屬性描述符
上,可以用來監(jiān)視没龙、修改或者替換方法定義铺厨。方法裝飾器不能用在聲明文件(*.d.ts
),重載或者任何外部上下文(比如declare
的類)中硬纤。
下面是方法裝飾器的定義:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n165" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">MethodDecorator = <T>(target: object, key: string, decriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;</pre>
參數(shù):
target: 對(duì)于靜態(tài)成員來說是類的構(gòu)造函數(shù)(construcor)解滓,對(duì)于實(shí)例成員是類的原型對(duì)象(Object)
key: 成員的名字(被裝飾的函數(shù)名)
descriptor: 成員(被裝飾的函數(shù))的屬性描述符
下面是一個(gè)方法裝飾器(@writable
)的例子,應(yīng)用于 Greeter
類的方法上:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n175" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function writable(value: boolean) {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.writable = value;
return descriptor;
}
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
// @writable(false)
greet() {
return Hello, ${this.greeting}
;
}
}
const greeter = new Greeter('Tony');
greeter.greet = () => { return 'Hello, Jack' };
console.log(greeter.greet());
// 注釋修飾器writable
// Hello, Jack
// 放開注釋
// Hello, Tony</pre>
這里的 @writable(false)
是一個(gè) 裝飾器工廠
咬摇。當(dāng)裝飾器 @writable(false)
被調(diào)用時(shí)伐蒂,它會(huì)修改屬性描述符的 writable
屬性。
注意點(diǎn):
裝飾器在 class 被聲明的時(shí)候執(zhí)行肛鹏,而不是class實(shí)例化的時(shí)候
方法裝飾器返回一個(gè)值
存儲(chǔ)原有的描述符并且返回一個(gè)新的描述符是推薦的做法逸邦。這在多描述符應(yīng)用場(chǎng)景下非常有用
設(shè)置描述符value的時(shí)候,不要使用箭頭函數(shù)
屬性裝飾器
通過屬性裝飾器在扰,可以重新定義 getter, setter, 修改 enumerable, writable, configurable 等屬性缕减,也可以用來記錄這個(gè)屬性的元數(shù)據(jù)。
屬性裝飾器定義如下:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n193" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">PropertyDecorator = (target: object, key: string) => void;</pre>
屬性裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)調(diào)用芒珠,傳入下列2個(gè)參數(shù)
對(duì)于靜態(tài)成員來說是類的構(gòu)造函數(shù)桥狡,對(duì)于實(shí)例成員是類的原型對(duì)象,即屬性擁有者
成員的名字皱卓,屬性名
用 Object.defineProperty 來實(shí)現(xiàn)一個(gè)簡(jiǎn)單的屬性裝飾器
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n203" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function realName(target, key: string): any {
let _val = target[key];
const getter = function() {
return ${_val} Ma
;
}
const setter = function(newVal) {
_val = newVal;
}
Object.defineProperty(target, key, {
get: getter,
set: setter
})
}
class JSMeetup {
// @realName
public myName = 'Tony';
greet() {
return Hi, I'm ${this.myName}
;
}
}
const meetup = new JSMeetup();
console.log(meetup.greet());
// 注釋裝飾器realName
// Hi, I'm Tony
// 放開注釋
// Hi, I'm Tony Ma</pre>
metadata(元數(shù)據(jù))
描述數(shù)據(jù)的數(shù)據(jù)裹芝,也叫元數(shù)據(jù)。它是對(duì)一直心系的一種集合稱謂娜汁,它可以是描述某項(xiàng)具體的數(shù)值嫂易,也可以是描述影像或聲音的內(nèi)容,也可能 只是一些注釋掐禁。它們往往跟隨著對(duì)象文件怜械,讓我們得以更全面的了解對(duì)象相關(guān)的信息颅和。eg. 照片的元數(shù)據(jù)包括圖像的尺寸、拍攝時(shí)間缕允、光圈峡扩、快門、GPS等障本;視頻文件畫面尺寸教届、視頻和音頻的編碼、時(shí)長(zhǎng)等等彼绷。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n208" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">import 'reflect-metadata';
const formatMetadataKey = Symbol('format');
function format(formatString: string) {
return Reflect.metadata(formatMetadataKey, formatString);
}
function getFormat(target: any, propertyKey: string) {
return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
}
class Greeter {
@format('Hello, %s')
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
let formatString = getFormat(this, 'greeting');
return formatString.replace('%s', this.greeting);
}
}
const greeter = new Greeter('Tony');
console.log(greeter.greet());
// Hello, Tony</pre>
這個(gè) @format('Hello, $s')
裝飾器是一個(gè) 裝飾器工廠
巍佑。被調(diào)用時(shí)茴迁,添加一條這個(gè)屬性的元數(shù)據(jù)寄悯。當(dāng) getFormat
被調(diào)用時(shí),它讀取對(duì)應(yīng)屬性的元數(shù)據(jù)堕义。
類裝飾器
類裝飾器應(yīng)用于類構(gòu)造函數(shù)猜旬,可以用來監(jiān)視,修改或替換類定義倦卖。如果類裝飾器返回一個(gè)值洒擦,它會(huì)使用提供的構(gòu)造函數(shù)來替換類的聲明。
類裝飾器定義如下:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n216" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">ClassDecorator = <T extends Function>(target: T) => T</pre>
類裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)調(diào)用怕膛,類的構(gòu)造函數(shù)作為其唯一的參數(shù)熟嫩。
無返回值:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n220" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">// 通過 Object.seal 密封此類的構(gòu)造函數(shù)和原型
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
// @sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
this.constructor.prototype.toString = function() {};
console.log(this.constructor.prototype);
}
greet() {
return Hello, ${this.greeting}
;
}
}
const greeter = new Greeter('Tony');
console.log(greeter.greet());
// 注釋裝飾器 sealed
// { toString: [Function (anonymous)] }
// Hello, Tony
// 取消注釋
// TypeError: Cannot add property toString, object is not extensible</pre>
有返回值:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n222" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">interface Extra {
extra: string
}
function AwesomeMeetup<T extends { new (...args: any[]): {} }>(constructor: T): T {
return class extends constructor implements Extra {
extra = 'Tadah!';
speaker: string = 'Ragularuban';
hello: string = 'Hello';
}
}
// @AwesomeMeetup
class JSMeetup {
public speaker = 'Ruban';
public hello: string;
constructor(message: string) {
this.hello = message;
}
greet() {
return Hi, I'm ${this.speaker}
;
}
}
const meetup = new JSMeetup('Hi') as JSMeetup & Extra;
console.log(meetup.greet());
console.log(meetup.extra);
class A extends JSMeetup {}
const a = new A('HeHe');
console.log(a.greet());
// 注釋裝飾器 AwesomeMeetup
/*
Hi, I'm Ruban
undefined
HeHe, I'm Ruban
*/
// 取消注釋
/*
Hello, I'm Ragularuban
Tadah!
Hello, I'm Ragularuban
*/</pre>
參數(shù)裝飾器
參數(shù)裝飾器應(yīng)用于類構(gòu)造函數(shù)或方法聲明,往往用來對(duì)特殊的參數(shù)進(jìn)行標(biāo)記褐捻,然后在方法裝飾器中讀取對(duì)應(yīng)的標(biāo)記掸茅,執(zhí)行進(jìn)一步的操作。
參數(shù)裝飾器往往搭配方法裝飾器一起使用
參數(shù)裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)被調(diào)用柠逞,傳入下列3個(gè)參數(shù):
對(duì)于靜態(tài)成員來說是類的構(gòu)造函數(shù)昧狮,對(duì)于實(shí)例成員是類的原型對(duì)象
成員的名字
參數(shù)在函數(shù)參數(shù)列表中的索引
參數(shù)裝飾器只能用來監(jiān)視一個(gè)方法的參數(shù)是否被傳入,參數(shù)裝飾器的返回值會(huì)被忽略板壮。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n237" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function logParameter(target: any, key: string, index: number) {
const metadataKey = 'myMetaData';
if (Array.isArray(target[metadataKey])) {
target[metadataKey].push(index);
} else {
target[metadataKey] = [index];
}
console.log('param target: ', target);
}
function logMethod(target, key: string, descriptor: any): any {
const originalMethod = descriptor.value;
// 參數(shù)裝飾器逗鸣、方法裝飾器的第一個(gè)參數(shù) target 是同一個(gè)引用
console.log('method target: ', target);
descriptor.value = function(...args: any[]) {
const metadataKey = 'myMetaData';
const indices = target[metadataKey];
for (let i = 0; i < args.length; i++) {
if (indices.indexOf(i) !== -1) {
args[i] = 'Abrakadabra';
}
}
const result = originalMethod.apply(this, args);
return result;
}
return descriptor;
}
class JSMeetup {
// @logMethod
public saySomething(something: string, @logParameter somethingElse: string): string {
return ${something} : ${somethingElse}
;
}
}
const meetup = new JSMeetup();
console.log(meetup.saySomething('Something', 'Something Else'));
// 注釋掉方法裝飾器 logMethod
/*
param target: { myMetaData: [1] }
Something : Something Else
*/
// 取消注釋
/*
param target: { myMetaData: [1] }
method target: { myMetaData: [1] }
Something : Abrakadabra
*/
</pre>
訪問器裝飾器
訪問器裝飾器應(yīng)用于訪問器的屬性描述符,可以用來監(jiān)視绰精、修改或替換一個(gè)訪問器的定義撒璧。
不能向多個(gè)同名的 get/set 訪問器應(yīng)用裝飾器,get/set 只能選擇其一應(yīng)用
訪問器裝飾器表達(dá)式會(huì)在運(yùn)行時(shí)當(dāng)作函數(shù)被調(diào)用笨使,傳入下列3個(gè)參數(shù):
對(duì)于靜態(tài)成員來說是類的構(gòu)造函數(shù)卿樱,對(duì)于實(shí)例成員是類的原型對(duì)象
成員的名字
成員的屬性描述符
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n251" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function configurable(value: boolean) {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
// descriptor.writable = false 會(huì)報(bào)錯(cuò)后再賦值就會(huì)報(bào)錯(cuò)
descriptor.configurable = value;
}
}
class Point {
private _x: number;
private _y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
@configurable(false)
get x() {
return this._x;
}
@configurable(false)
get y() {
return this._y;
}
}
function reWrite(value: number) {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.get = function() {
return value;
}
return descriptor;
}
}
class Greeter {
_x: number;
_y: number;
constructor(x: number, y: number) {
this._x = x;
this._y = y;
}
set x(value: number) {
this._x = value;
}
// @reWrite(999)
get x() {
return this._x;
}
getPoint() {
console.log(x: ${this.x}, y: ${this._y}
);
}
}
const greeter = new Greeter(100, 100);
greeter.getPoint();
// 注釋掉訪問器裝飾器 reWrite
// x: 100, y: 100
// 去取消注釋
// x: 999, y: 100</pre>
TypeScript 解決了哪些痛點(diǎn)
屬性、方法等訪問/調(diào)用錯(cuò)誤阱表,空指針等基于非預(yù)期類型的錯(cuò)誤
常見錯(cuò)誤:
Uncaught TypeError: Cannot read property
TypeError: 'undefined' is not an object
TypeError: null is not an object
TypeError: 'undefined' is not a function
TypeError: Cannot read property 'length'
ReferenceError: event is not defined
enum 枚舉
常見的應(yīng)用場(chǎng)景是描述某同一特征的常量殿如、元數(shù)據(jù)贡珊,例如任務(wù)類型可能被定義為 '1', '2', '3',不同的任務(wù)類型有不同的處理邏輯涉馁,通過 switch/if 判斷時(shí)门岔,我們直接 if(taskType === '1') 是不可以的,枚舉(enum) 的出現(xiàn)解決了這個(gè)問題烤送,并且讓代碼的可閱讀性更高寒随。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n273" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">enum TaskType {
DOWNLOAD: '1',
SHARE: '2',
FEED: '3'
}
switch (type) {
case DOWNLOAD:
// todo
break;
// ...
}
enum ResStatus {
EXPECT: '1001',
ACT_OVER: '3001',
NO_LOGIN: '3010'
}</pre>
裝飾器
一些底層的 Class 不能過多的業(yè)務(wù)邏輯或者耦合,或者在某些場(chǎng)景中帮坚,需要對(duì)元數(shù)據(jù)進(jìn)行操作(元編程)妻往,或在真實(shí)應(yīng)用中,不能盡善盡美试和,裝飾器可以看做是一種潤(rùn)滑劑讯泣、補(bǔ)丁、對(duì)元數(shù)據(jù)的操作(元編程)阅悍,或者說可以稱之為面向切面編程(AOP)好渠。
泛型
TypeScript 高級(jí)用法(字節(jié)前端)
泛型在 TS 中承載了從靜態(tài)定義到動(dòng)態(tài)調(diào)用的橋梁,同時(shí)也是 TS 對(duì)自己類型定義的元編程节视。
基本使用
泛型可以用在普通類型定義拳锚,類定義、函數(shù)定義上:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n285" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">// 普通類型定義
type Dog<T> = { name: string, type: T }
// 普通類型使用
// 需要把泛型類型也寫上去
const dog: Dog<number> = { name: 'wang', type: 3 }
// 類定義
class Cat<T> {
private type: T;
constructor(type: T) {
this.type = type;
}
}
// 類使用
// 變量能夠推斷出來寻行,可以省略泛型書寫
const cat: Cat<number> = new Cat<number>(20); // const cat = new Cat(20);
// 函數(shù)定義
function swipe<T, U>(value: [T, U]): [U, T] {
return [value[1], value[0]];
}
// 函數(shù)使用
// 變量能夠推斷出來霍掺,可以省略泛型書寫
swipe<Cat<number>, Dog<number>>([cat, dog]); // swipe([cat, dog]);</pre>
泛型約束
有的時(shí)候,可以不用關(guān)注泛型具體的類型
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n289" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function fill<T>(length: number, value: T): T[] {
return new Array(length).fill(value);
}</pre>
有時(shí)候拌蜘,需要限定類型杆烁,可以使用關(guān)鍵字 extends
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n291" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">function sum<T extends number>(value: T[]): number {
let count = 0;
value.forEach(v => count += v);
return count;
}</pre>
這樣可以 sum([1, 2, 3])
的方式調(diào)用,而 sum(['1', '2', '3'])
則無法通過編譯拦坠。
泛型推斷 infer
一般搭配泛型條件語(yǔ)句使用连躏,所謂推斷,就是不用預(yù)先指定在泛型列表中贞滨,在運(yùn)行時(shí)會(huì)自動(dòng)判斷入热,不過先得預(yù)先定義好整體的結(jié)構(gòu)。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n296" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">type Foo<T> = T extends {t: infer Test} ? Test: string;</pre>
首先看 extends 后面的類容晓铆,{t: infer Test}
可以看成一個(gè)包含 t
屬性的類型定義勺良,這個(gè) t 屬性的 value 類型通過 infer 進(jìn)行推斷后會(huì)賦值給 Test 類型,如果泛型實(shí)際參數(shù)符合 {t: infer Test}
的定義骄噪,那么返回 Test 類型尚困,否則默認(rèn)返回 string 類型。
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n298" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">// 返回 string链蕊,因?yàn)?number 不是一個(gè)包含 t 屬性的對(duì)象類型
type One = Foo<number>;
// 返回 boolean,因?yàn)榉盒蛥?shù)中有 t 屬性的對(duì)象類型,使用了 infer 對(duì)應(yīng)的類型年局,即 boolean
type Two = Foo<{t: boolean}>
// 同理,返回一個(gè)函數(shù)類型掌实,() => void
type Three = Foo<{a: numbem, t: () => void}></pre>
Tips
Vue 提供 interface
RendererElement
,可用于 渲染函數(shù)/tsx 中作為返回值校驗(yàn)聯(lián)合類型中巧用
never
類型邦马,可以處理一些遺漏的case
interface
HTMLInputElement
的 value 屬性獲取輸入框的值 (event.target as HTMLInputElement).value數(shù)據(jù)結(jié)構(gòu)過于復(fù)雜贱鼻、嵌套太深,類型判斷無法推斷時(shí)滋将,可以試著使用
?.
?.()
obj as IObject
等方式解決usehooks 中如果涉及到元素獲取邻悬,最好使用 vue 單文件組件,ref 可以指向唯一值随闽,tsx 等渲染函數(shù)中無法通過 ref 定位元素父丰,當(dāng)組件復(fù)用高時(shí),通過 document.querySelector 拿到的不一定是預(yù)期元素(可以通過唯一 id 或 props 來解決)
setup 方法不能 async
-
數(shù)組是一串相同類型的數(shù)據(jù)的集合橱脸,內(nèi)存中連續(xù)存放础米,實(shí)際上存放的是地址,指向?qū)ο髮?shí)例添诉,JS 中數(shù)組并不是真正意義上的數(shù)組,故 TypeScript 中的定義數(shù)組時(shí)医寿,需要指定數(shù)組元素中的類型
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="javascript" cid="n318" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">// js 的數(shù)組可以是任意類型
const hunmanList = [18, 'Tony', { like: 'Reading' }]</pre><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n320" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">interface Human {
age: number,
name: string
}const humanList: Human[]
const humanList: Array<Human></pre> -
defineAsyncComponent
用于引用子組件處栏赴,接收的參數(shù)是一個(gè) Promise<Component><pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n323" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">const BannerDownload = defineAsyncComponent((): Promise<Component> => import('@/components/common/BannerDownload.vue'));</pre>
通 react 一樣,類型斷言最好使用
el as HTMLElement
而非尖括號(hào)<HTMLElement>el
Vue3 暴露的
interface
,type
在文件 @vue/runtime-core/dist/runtime-core.d.ts 中-
當(dāng)提示不能對(duì) null 進(jìn)行類型斷言時(shí)靖秩,先對(duì)變量類型簽名
unknown
须眷,使用unknown
替代any
,既靈活又可以保證類型安全- Vue3
setup
中獲取模板引用ref
沟突,需要 return花颗,示例如下:
<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="typescript" cid="n333" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">onMounted(() => {
const videoBox = ref(null);
onMounted(() => {
const el: unknown = videoBox.value;
console.log(window.getComputedStyle(el as HTMLElement).width);
})
return { videoBox }
})</pre> - Vue3
TypeScript 內(nèi)置類型在
node_modules/typescript/lib
下的*.d.ts
文件中全局安裝
ts-node
然后在.ts
文件中第一行*#!/usr/bin/env ts-node*
終端中輸入路徑即刻直接執(zhí)行ts
文件構(gòu)造函數(shù)的類型可以通過
{ new (...args: any[]): {} }
來描述