<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          關于 TypeScript 一些高級用法的總結

          共 8819字,需瀏覽 18分鐘

           ·

          2021-09-03 12:51

          前言

          TypeScript 是 JavaScript 的類型的超集,它可以編譯成純 JavaScript。編譯出來的 JavaScript 可以運行在任何瀏覽器上。TypeScript 編譯工具可以運行在任何服務器和任何系統上。筆者使用typescript也差不多快一年了,使用Ts也感受頗深,Ts無疑增強我們代碼的規(guī)范性和可維護性,但是也同時增加了我們的開發(fā)負擔,但是其實對一個項目的長期而言,我認為這是值得的,這篇文章我們來講一下Ts的高級用法,適合剛接觸Ts的同學但是學的沒有那么深的同學,至于我們的vue源碼分析,我先暫更一周,在以后的時間將會繼續(xù)講到。

          一、類型

          1. unknow

          unknown 指的是不可預先定義的類型,在很多場景下,它可以替代 any 的功能同時保留靜態(tài)檢查的能力。

          const num: number = 10;(num as string).split('') // error 類型 "number" 到類型 "string" 的轉換可能是錯誤的,因為兩種類型不能充分重疊。如果這是有意的,請先將表達式轉換為 "unknown"。(num as unknown as string).split('')

          這個時候 unknown 的作用就跟 any 高度類似了,你可以把它轉化成任何類型,不同的地方是,在靜態(tài)編譯的時候,unknown 不能調用任何方法,而 any 可以。

          const foo: unknown = 'string';foo.substr(1);     // Error: 靜態(tài)檢查不通過報錯const bar: any = 10;bar.substr(1);    // Pass: any類型相當于放棄了靜態(tài)檢查

          unknown 的一個使用場景是,避免使用 any 作為函數的參數類型而導致的靜態(tài)類型檢查 bug:

          function test(input: unknown): number {  if (Array.isArray(input)) {    return input.length;    // Pass: 這個代碼塊中,類型守衛(wèi)已經將input識別為array類型  }  return input.length;      // Error: 這里的input還是unknown類型,靜態(tài)檢查報錯。如果入參是any,則會放棄檢查直接成功,帶來報錯風險}
          2. enum

          枚舉的話可以增強我們代碼的可讀性,例如我在業(yè)務中需要個后端傳一個回答問題的類型type,回復文本的話需要傳一個1,回復圖片的會傳2,如果直接在調用接口的時候傳一個2那肯定會讓別人看上去很疑惑,如此一來我們可以定義一個枚舉,需要傳值例如傳一個視頻,我們可以傳AnswerMessageType.video,而且在寫接口規(guī)范參數類型的時候我們可以規(guī)定這個type為AnswerMessageType,如此一來你要是傳個7就肯定不能通過的。

          /** * 回復問題的消息類型 */ export enum AnswerMessageType {    text = 0,    pictrue = 1,    video = 2,    voice = 3,    file = 4,    /**     * Srceent Rock 鏈接     */    srlink = 6  }  
          3. void

          在 TS 中,void 和 undefined 功能高度類似,可以在邏輯上避免不小心使用了空指針導致的錯誤。void 和 undefined 類型最大的區(qū)別是,你可以理解為 undefined 是 void 的一個子集,當你對函數返回值并不在意時,使用 void 而不是 undefined。舉一個 React 中的實際的例子。

          // Parent.tsxconst  Parent: FC = () => {    const getValue = (): number => { return 2 };     /* 這里函數返回的是number類型 */    // const getValue = (): string => { return 'str' };  /* 這里函數返回的string類型,同樣可以傳給子屬性 */    return <Child getValue={getValue} />}// Child.tsxtype Props = {  getValue: () => void;  // 這里的void表示邏輯上不關注具體的返回值類型,number、string、undefined等都可以}function Child({ getValue }: Props) => <div>{getValue()}</div>

          運算符

          1. 非空斷言運算符 !

          這個運算符可以用在變量名或者函數名之后,用來強調對應的元素是非 null|undefined 的

          function onClick(callback?: () => void) {    callback();    // 參數是可選入參,加了這個感嘆號!之后,TS編譯不報錯}

          這個符號的場景,特別適用于我們已經明確知道不會返回空值的場景,從而減少冗余的代碼判斷,如 React 的 Ref。以下是筆者工作中寫的一個自定義hook,左右兩個div,左邊放圖片,右邊是一段文字文字的多少是不確定的,左邊的圖片根據右邊的盒子來計算大小,如下使用了多個ref,但是我們在一些情況寫可以!來判斷e.target!或者divRef.current!來進行一個書寫

          export function useCalcImg() {  const [maxWidth, setMaxWidth] = useState(0)  const [maxHeight, setMaxHeight] = useState(0)  const [imgWidth, setImgWidth] = useState(0)  const [imgHeiht, setImgHeight] = useState(0)  const imgRef = useRef(null)  const divRef = useRef<HTMLDivElement>()  const handleImgLoad = useCallback((e) => {    let img = e.target as HTMLImageElement    setImgWidth(img.naturalWidth)    setImgHeight(img.naturalHeight)  }, [])  const calc = () => {    if (maxWidth && imgWidth && imgRef.current) {      let div = divRef.current      setTimeout(() => {        let { width, height } = Util.calc.fixedImgScale(imgWidth, imgHeiht, div.clientWidth, div.clientHeight)        imgRef.current.width = width        imgRef.current.height = height        removeClass(imgRef.current, 'hidden')      }, 100)      imgRef.current.width = 100    }  }
          const imgRefCallback = useCallback((img) => { imgRef.current = img }, []) useEffect(() => { calc() }, [imgRef, divRef, maxWidth, imgHeiht]) const refCallback = useCallback((e) => { divRef.current = e if (e) { const { clientWidth, clientHeight } = e setMaxWidth(clientWidth) setMaxHeight(clientHeight) } }, []) return { imgRefCallback, handleImgLoad, refCallback }}
          2. 可選鏈操作運算符 ?

          相比上面!作用于編譯階段的非空判斷,?.這個是開發(fā)者最需要的運行時(當然編譯時也有效)的非空判斷。

          // 例如后端有個參數標識flag,有標識的時候傳flag: 1, 沒有標識的時候不傳data?.flag

          ?.用來判斷左側的表達式是否是 null | undefined,如果是則會停止表達式運行,可以減少我們大量的&&運算。

          比如我們寫出a?.b時,編譯器會自動生成如下代碼

          let b = a !== null && a !== void 0 ? a : 10;

          三、操作符

          1. keyof

          keyof 可以獲取一個類型所有鍵值,返回一個聯合類型,如下:

          type Person = {  name: string;  age: number;}type PersonKey = keyof Person;  // PersonKey得到的類型為 'name' | 'age'
          2. typeof

          typeof 是獲取一個對象/實例的類型,如下

          interface Person {    name: string,    age: number}const people: Person = {    name: 'thl',    age: 22}const P = typeof people // { name: string, age: number | undefined }const me: typeof people = { name: 'wsy', age: 21 }
          3. in

          in 只能用在類型的定義中,可以對枚舉類型進行遍歷,如下:

          // 這個類型可以將任何類型的鍵值轉化成number類型type TypeToNumber<T> = {  [key in keyof T]: number}

          keyof返回泛型 T 的所有鍵枚舉類型,key是自定義的任何變量名,中間用in鏈接,外圍用[]包裹起來(這個是固定搭配),冒號右側number將所有的key定義為number類型。

          const person: TypeToNumber<Person> = {name: 21, age: 21}// [ 自定義變量名 in 枚舉類型 ]: 類型

          四、泛型

          泛型在 TS 中可以說是一個非常重要的屬性,它承載了從靜態(tài)定義到動態(tài)調用的橋梁,同時也是 TS 對自己類型定義的元編程。泛型可以說是 TS 類型工具的精髓所在,也是整個 TS 最難學習的部分?;臼褂?/span>

          // 普通類型定義type Person<T> = { name: string, type: T }// 普通類型使用const person: Person<number> = { name: 'ww', type: 20 }
          // 類定義class People<T> { private type: T; constructor(type: T) { this.type = type; }}// 類使用const people: People<number> = new People<number>(20); // 或簡寫 const people = new People(20)
          // 函數定義function swipe<T, U>(value: [T, U]): [U, T] { return [value[1], value[0]];}

          例如深度克隆

          export function deepCopy<T extends Record<any, any>>(data: T): T {  const t = typeOf(data)  let o
          if (t === 'array') { o = [] } else if (t === 'object') { o = {} } else { return data }
          if (t === 'array') { for (let i = 0; i < ((data as any) as any[]).length; i++) { o.push(deepCopy(data[i])) } } else if (t === 'object') { for (let i in data) { o[i] = deepCopy(data[i]) } } return o}

          五、高級泛型

          1. Partial

          此工具的作用就是將泛型中全部屬性變?yōu)榭蛇x的。

          type Partial<T> = {    [P in keyof T]?: T[P]}

          舉個栗子,此時happy這個方法可以不傳

          type Person = {    name: string    age: number    happy: () => void}const me: Partial<Person> = {    name: 'thl',    age: 22}
          2、Record<K, T>

          此工具的作用是將 K 中所有屬性值轉化為 T 類型,我們常用它來申明一個普通 object 對象。

          type Record<K extends keyof any,T> = {  [key in K]: T}

          舉個栗子

          const me: Record<string, string> = { 'name': 'thl', 'tag': '打工人' }
          3. Pick<T, K>

          此工具的作用是將 T 類型中的 K 鍵列表提取出來,生成新的子鍵值對類型。

          type Pick<T, K extends keyof T> = {  [P in K]: T[P]}

          舉個栗子

          interface Person {    name: string    age: number    do: () => void}type PeopleInfo =  Pick<Person, 'name' | 'age'>// 將name和age兩個類型提取出來,不用從新定義類型const me: Person { name: 'thl', age: 22}
          4: Exclude<T, U>

          此工具是在 T 類型中,去除 T 類型和 U 類型的交集,返回剩余的部分。

          type Exclude<T, U> = T extends U ? never : T

          舉個栗子

          type PersonOne = {    name: string    age: number    sleep: () => void}type PersonTwo = {    name: string    age: number    eat: () => void}
          type Person = Exclude<PersonOne, PersonTwo> 等價于type PersonDo = { sleep: () => void eat: () => void }
          5. Omit<T, K>

          此工具可認為是適用于鍵值對對象的 Exclude,它會去除類型 T 中包含 K 的鍵值對。

          type Omit = Pick<T, Exclude<keyof T, K>>

          舉個栗子

          type Person = Omit<PersonOne, 'age'>const me: Person = {    name: 'thl',    sleep: () => console.log('i will')}
          6. ReturnType

          此工具就是獲取 T 類型(函數)對應的返回值類型:

          type ReturnType<T extends (...args: any) => any>  = T extends (...args: any) => infer R ? R : any;

          看源碼其實有點多,其實可以稍微簡化成下面的樣子:

          type ReturnType<T extends func> = T extends () => infer R ? R: any;

          舉個栗子

          function foo(x: string | number): string | number {// Todo}type FooType = ReturnType<foo>;  // string | number
          7. Parameters

          此工具就是獲取T類型(函數)對應的參數類型

          type Parameters<T> = T extends (...args:string[]) => any ? string[] : any;

          老規(guī)矩舉個栗子

          type Fn = (a: string, b: number) => voidtype FnParams = Parameters<Fn>

          其實還有一些高級類型這里就不一一列舉了,有興趣的可以去逛下官網www.typescriptlang.org

          總結

          Ts的使用有很多技巧,我們在日常使用中可能沒有那么容易遇到,在我們閱讀一些源碼的時候就會了解到很多,還有一個需要總結的問題是關于type和interface的區(qū)別和我們如何選擇的問題,本質上沒有什么區(qū)別, 從擴展的角度上來看,type比interface好些,使用&可以少些一些代碼

          type Person = {    name: string    age: number}// 擴展type NewPerson = Person & {do: () => void}interface People {    happyStatus: true    eat: () => void}// 擴展interface NewPeople extends People {    name: string}

          另外 type 有一些 interface 做不到的事情,比如使用 | 進行枚舉類型的組合,使用typeof獲取定義的類型等等。

          不過 interface 有一個比較強大的地方就是可以重復定義添加屬性,比如我們需要給window對象添加一個自定義的屬性或者方法,那么我們直接基于其 Interface 新增屬性就可以了。

          declare global {    interface Window { MyNamespace: any; }}

          個人習慣一般我使用type多一些

          栗鼠怪

          轉自:https://juejin.cn/post/6983576636025208846

          瀏覽 70
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  97啪啪视频 | 2019天天操 | 丁香婷婷色 | 亚洲欧美v在线视频 | 91天天干 |