元件引數的編譯期檢查
隨著TypeScript語言特性的完善,我們團隊的前端程式碼已完全遷移至TypeScript。當前TypeScript完全支援JSX語法,舉個例子:
import * as React from 'react'
type Props = {
p1: string
}
class ComponentA extends React.Component<Props> {
render(){
return <div>{this.props.p1}</div>
}
}
<ComponentA /> //編譯時錯誤,p1必須傳入
<ComponentA p1="test"/> //正確
複製程式碼
這樣我們宣告的ComponentA元件就可以在編譯時檢查傳入引數是否正確。
如果我們希望元件的某個引數是選填的,只需要在引數定義前加一個問號即可:
import * as React from 'react'
type Props = {
p1?: string
}
class ComponentA extends React.Component<Props> {
render(){
return <div>{this.props.p1 || "empty"}</div>
}
}
<ComponentA /> //正確,p1為非必需引數
<ComponentA p1="test"/> //正確
複製程式碼
高階元件
在使用React的過程中,我們不可避免的會對現有元件進行再封裝,形成很多高階元件。
對於這種情況,我們首先想到的方案是編寫獨立的高階元件,如:
import * as React from 'react'
type BCProps = {
p1: string
}
class BC extends React.Component<BCProps> {
render(){
return <div>{this.props.p1}</div>
}
}
type HOCProps = {
p1h:string
}
class HOC extends React.Component<HOCProps> {
render(){
return <BC p1={this.props.p1h}/>
}
}
複製程式碼
在上面的例子中,BC是基礎元件,HOC是我們封裝的高階元件。我們可以看到BCProps與HOCProps是相互獨立的,這樣做的好處有以下兩點:
- 適用性廣,任何場景都可使用此種封裝方式。
- 完全解耦,使用者只需要瞭解HOCProps,對BCProps無需關心。
但相對的,其缺點也非常明顯,由於對BC進行了完全封裝,當BCProps改變時,如p1更改了名稱,改為了p1b,此時我們需要更改HOC的程式碼,否則執行會報錯。
舉一個很常見的例子,一個基礎元件自帶樣式並不符合我們的需求,我們希望能封裝一個自定義樣式的高階元件,而其他Props保持不變。這樣的話,在樣式引數的定義不變而其他引數變動的情況下,可以做到高階元件的程式碼無需進行修改。
在JS中,實現這種引數傳遞非常的簡單,但是在TS中會有些麻煩。最笨的方法是我們手動將BCProps中的樣式無關引數複製一份,但是這樣的話如果BCProps被修改,那麼我們需要手動修改HOCProps,無法做到非樣式引數改變,程式碼無需修改的目的。
在當前的TS版本中,我們無法直接對已存在型別的屬性進行刪除操作,如果要實現這種高階元件,首先我們需要定義兩個幫助型別:
type Diff<T extends string, U extends string> = ({[P in T]: P } & {[P in U]: never } & { [x: string]: never })[T];
type Omit<T, K extends keyof T> = {[P in Diff<keyof T, K>]: T[P]};
複製程式碼
這樣,我們就可以實現不含樣式引數的高階元件:
import * as React from 'react'
type BCProps = {
p1: string,
style: React.CSSProperties
}
class BC extends React.Component<BCProps> {
render(){
return <div style={this.props.style}>{this.props.p1}</div>
}
}
type HOCProps = Omit<BCProps, "style">
class HOC extends React.Component<HOCProps> {
render(){
return <BC {...Object.assign({},this.props,{style:{color:"red"}})}/>
}
}
複製程式碼
只要style引數的使用方法不變,當BC被修改時,HOC無需任何改變。