Typescript 3 使用,那些奇怪的符號: operators, private, decorator, generic type
本篇會比較強調 typescript 中一些特殊符號,還有>ES6的一些符號,例如說optional chainning,private等的特殊符號。說實話,這篇真的覺得難寫,我不該把範圍開那麼大,不過常用的特殊符號分開來就感覺凌亂,還是寫在一篇我覺得比較好...吧?
|
union type 的使用
Union type 使用的時候,即代表參數同時接受 |
兩邊的型別定義,但是注意,他只能符合其中一個型別
let strOrNumber: string | number | undefined; // 同時接受 string, number, undefined的型別
進階使用方式我常用在撰寫 function 中若有不同的 state,代入的argument也會不同
// 通常出現在 react reducer 的 action 上
type IAction = {
type: 'init' // 這些參數甚至可以搭配enum操作
} | {
type: 'update',
payload: {
arg: string
}
} | {
type: 'disable',
payload: {
isShow: boolean;
}
}
function reducer(state = initialState, action: IAction) {
// do content
switch (action.type) {
case 'init':
// no payload
break;
case 'update':
action.payload.arg // string type
break;
case 'disable':
action.paylaod.isShow // boolean type
break;
}
}
&
intersection types 的使用
這個使用是將兩者 object 結合在一起即為Object.assign
的功能,不過是在typescript定義型別中才會用到的屬性。
個人使用通常會先定義個基礎型別,再基於它做擴充
type IBasicType = {
id: number;
value: string;
}
type IExtend = IBasicType & {
foo: string;
}
? 和 ! 的使用 optional
type assertion
non-null assertion
?
在不同的地方有不同的用處,在 typescript 定義檔中,如果看到以下的宣告方式,就是代表此值為 optional
,可能存在或者是 undefined
interface IFoo {
bar?: string;
}
function getFoo (payload: IFoo) {
const bar:string = payload.bar; // 這裡會噴錯誤,因為payload.bar可能是undefined
}
如果是搭配 if / else
typescript 能夠有辦法自行在該 block 推斷型別
function getFoo (payload: IFoo) {
if (payload.bar !== undefined) {
// 這裡即會將payload.bar推斷為string type
}
}
如果在實作中已經 100% 確定接下來的值不是 undefined
可是卻無法被 typescript 正確推斷,這類型情況比較常發生在 Map
、Array.prototype.find
上,通常自行設定的會是1:1的對應關係可是一直會有錯誤顯示說可能為undefined
這時候有幾種解法。
先以我常用的Map型別做個簡易的示範
const map = new Map([
[1, { foo: true }],
[3, { foo: false }],
]);
取
1
的值
const first = map.get(1);
此時可以看到顯示的type型別會如下
const first: {
foo: boolean;
} | undefined;
這樣在後續進行實作上又要為了無謂的型別確認浪費行數做這件事,這樣的話我會用以下2種解決辦法
type assertion
類型斷言直接強制型別
const first = map.get(1) as { foo: boolean }
const foo = map.get(1)!
而在 typescript 3.7
版本中新增當前為 tc39/stage 4
的 optional chainning
。
map.get(1)?.foo // undefined or boolean
map.get(1)?.['foo'] //亦可,如果是array就改number
在這邊使用?
即代表該值可能為 undefined
而在此就會中斷避免後續執行造成error,而除了可以對value進行選擇外,對於執行 function
的話可以改成如下
const map = new Map([[1, function () { return true; }]);
map.get(1)?.() // 當確定有值才會執行function的內容
相關處理在 Aray.prototype.find
也會出現,如何使用就看當下情景做選擇了。
#
private fields
這個是當前已經進入tc39-stage3 的功能,未來不久就會出現的規範,早前 typescript 為了 private
有直接取用這個關鍵字來宣告是 private field 即不可被外部存取。現在出現#
的關鍵字就直接引用了
class Foo {
#bar: string;
constructor () {
this.#bar = 'bar';
}
}
const foo = new Foo()
foo.bar // get Error here
一樣,現在寫js都比較偏向functional programming,這個private型別對我來說已經算不太常會用到的功能了
@
decorators
裝飾器,僅能使用於class的項目之一,這個解釋起來略複雜,有機會後面再說(明明就是不會用)
Generic Type: 泛型 <T>
使用上通常是拿來做爲有可擴充功能的型別來使用,使用泛型的好處就是避免在最初定義時就寫死他能夠使用的型別,讓使用該funciton的地方能夠自行決定其中的T
型別爲何。
沒有泛型的情況,我們只能以特定型別設定
function identity(arg: number): number {
return arg;
}
這樣只能限定identity使用number傳入,可是其回傳的並不只能number
可以使用。OK,那就改用any
function identity(arg: any): any {
return arg;
}
那這樣就將ts的好處無視掉了,後續接手的也會不清楚return會如何,只能花費時間深入研究。這時就可以使用泛型解決這個問題
function identity<T>(arg: T): T {
return arg;
}
這樣在使用上,我可以清楚的知道回傳會有什麼東西,或者一開始就先預設好應該有的型別
identity<string>('mystring');
如此一來,我就能設定好該項僅能接收string
這個型別的東西而不用另外建立一個function處理型別問題。
除了function外 interface
,type
,class
皆可使用泛型喔
// interface
interface IFoo<T> {
bar: T;
}
// type
type TFoo<T> = { bar: T }
// class
class Foo<T> {
private bar: T;
}
簡單就是這樣,接下來講實務用法。
泛型可以直接針對需要的型別做擴充,或者直接預設可以繼承相關的型別,抑或是搭配string提示,後面可以採用string literal的宣告方式
- 擴充型別
使用
extends
來做繼承/擴充
function identity<T extends { search: string }>(arg: T) {}
// 使用就必須使用search且型別為string的object了
identity({search: 'a', bar: 'ok'});
- 預設型別,另外function 使用搭配string literal實現
// 這樣就不會強迫需要設定generic type
function identity<T extends string = ‘’>(terms: T) {}
type TAllowToUse = 'Foo' | 'Bar';
identity<TAllowToUse>('Foo');
identity('I can do every thing');
- Axios回傳Data預設型別
現在使用的ajax library已經習慣使用Axios了,後續在實作上套用的時候,request回來的資料型別都是any,Axios 有提供泛型讓使用者加入回傳 type
const response = await Axios.get<{hello: string}>('url').then(res => res.data); // res.data就是{hello: string}的型別
interface
使用
interface IFoo<T> {
// extends use intersection
bar: T & { search: string };
}
泛型使用很靈活,但是也很容易濫用,就看使用者如何設計了。
留言
張貼留言