TypeScript 開發經驗分享

typescript

語法

一、 a標籤添加clstag這種自定義標籤類型錯誤問題

添加了declare namespace JSXdeclare module 'react'javascript

不起做用,並且引入新的ts校驗錯誤css

按照html5標準建議,只要是自定義標籤,都應使用data-開頭的定義html

  • 最終解決辦法,將
<a clstag={`....`}>{children}</a>
複製代碼

改成以下格式,勉強騙過校驗,達到不報錯的地步。 正路仍是應該尋找擴展HTMLAnchorElement屬性的方法前端

const props = {
  clstag: '...'
}
<a {...props}>{children}</a>
複製代碼

二、 關於mobx-react@inject已注入屬性,但在使用該組件時仍是類型校驗提示缺乏屬性的問題

  • 參考 經過一個自定義get injected屬性來避免直接獲取props的方法,須要在每一個組件中都多寫一個方法html5

  • 採用方案,在注入屬性後使用!代表該屬性確認存在。java

    const { router } = this.props.store!
    複製代碼

三、 在運行時經過擴展添加屬性的對象,用&

例如在store/header中使用了storePropnode

export interface IHeader {
  logoPlayed: boolean
  setLogoPlayed: (v: any) => void
  restoreLogoPlayed: () => void
}

@storeProp({
  setter: [
    {
      default: !!storage.get('logoPlayed'),
      name: 'logoPlayed',
    },
  ],
})
class Header {
  public headerProp = false
}

const header = new Header() as IHeader & Header

export { Header }

export default header
複製代碼

在使用header時便可獲取IHeaderHeader中的全部屬性react

四、要求每一個實例的屬性都有初始值,初始值既能夠在 constructor 中設置,也能夠在聲明時設置

Property 'fetchBatch' has no initializer and is not definitely assigned in the constructor.
複製代碼

若是一個普通類中的方法只是被定義了,而沒有進行任何實現,便會報以上錯誤,解決辦法之一是這樣寫:webpack

public restoreBatch!: () => void
複製代碼

若是每次都這樣寫確實有些麻煩,因此能夠在tsconfig.json添加如下配置:git

"strictPropertyInitialization": false,

複製代碼

可是,以更嚴謹的角度來講,未進行實現的任何方法都不該該寫在普通類裏,而應該放在抽象類裏。所以,遇到以上狀況時,代碼應該如下方式來實現:

abstract class A{
  public restoreBatch: () => void
}

class B extends A{}
const b = new B()
export default b
複製代碼

五、變量導出不能做爲類型,即便這個變量是從一個class賦值而來

class A {}
export const A1 = A

// other file
import { A1 } from 'A'
// 此時A1僅僅做爲一個變量使用,沒法做爲類型來使用的。
// 是個坑 🥵 !
複製代碼

六、高階組件的children類型定義

例如src/component/Permission組件的children類型應該定義爲children: React.ReactNode。在 React 中將ReactNode的類型定義成了:

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;
複製代碼

七、antd 的Form.create()修飾器

此修飾器目前在 ts 中沒法使用,只能換成 function 的形式

例如:

@Form.create()
class Test{}
複製代碼

換成:

class Test{}

Form.create()(Test)
複製代碼

官方解釋

八、解決如下異常的兩種方法

異常信息:

Property 'store' is missing in type '{}' but required in type 'Readonly<RcBaseFormProps & Pick<IProps, "store">>'.
複製代碼

Search.tsx源碼:

interface IProps {
  store: {
    rollbackEdit: RollbackEditStore
  }
  form: WrappedFormUtils
}

@inject('store')
@observer
class Search extends React.Component<IProps> {
  public store: RollbackEditStore
  constructor(props: IProps) {
    super(props)
    const {
      store: { rollbackEdit },
    } = this.props
    this.store = rollbackEdit
  }

  public onChange = (value: SelectValue) => {
    const { form } = this.props
    this.store.setType(value)
    this.store.setList([])
    form.setFieldsValue({
      id: undefined,
    })
  }

  public render() {
    ...
  }
}

export default Form.create()(Search)

複製代碼

第一種解決方法:

去掉constructor中對 store 引用的定義。在IPropsstore後面加一個可選符問號

interface IProps {
  store?: {
    rollbackEdit: RollbackEditStore
  }
  form: WrappedFormUtils
}
複製代碼

將方法中引用的this.store都修改成const { rollbackEdit } = this.props.store!

第二種解決方法:

不修改Search.tsx中的代碼,只須要在調用它的index.tsx中將 store 給它傳入進去便可:

const store = {
  rollbackEdit,
}

const RollbackEdit = () => (
  <Card title={ <CardTitle> <Search store={store} /> </CardTitle> } bordered={false} > <List /> </Card> ) 複製代碼

九、typescript引發的webpack split chunk失效。

參考

與ts官網等,得知tsconfig的

module: 'commonjs'
複製代碼

必然會致使代碼分割實效,必須配置爲

module: 'esnext'
複製代碼

webpack.config.ts中又使用了commonjs的包,ts文檔中明確說明,使用

export = xxxx
複製代碼

導出的包,必須使用

import xxx = require('xxx')
複製代碼

的方式來引入。但只要有這種包的使用,就沒法使用esnext的模塊類型。

可選方案:

  1. 使用tsconfig-paths-webpack-plugin,引入另外一個tsconfig.json文件,該文件使用esnext的模塊方式。
  2. 較簡潔的方式,在webpackts-loader中添加options項,來達到使前端編譯環境與源碼環境使用不一樣tsconfig配置的目的。
options: {
  compilerOptions: {
    module: 'esnext',
  },
},
複製代碼

十、函數類型的雙變性

具體可查看:函數類型的雙變性

十一、類型守衛與類型區分

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function getSmallPet(): Fish | Bird {
    // ...
}

let pet = getSmallPet();

// 每個成員訪問都會報錯
if (pet.swim) {
    pet.swim();
}
else if (pet.fly) {
    pet.fly();
}
複製代碼

爲了讓這段代碼工做,咱們須要使用類型斷言:

let pet = getSmallPet();

if ((<Fish>pet).swim) {
    (<Fish>pet).swim();
}
else {
    (<Bird>pet).fly();
}
複製代碼

這裏能夠注意到咱們不得很少次使用類型斷言。 倘若咱們一旦檢查過類型,就能在以後的每一個分支裏清楚地知道pet的類型的話就行了。 TypeScript裏的類型守衛機制讓它成爲了現實。 類型守衛就是一些表達式,它們會在運行時檢查以確保在某個做用域裏的類型。 要定義一個類型守衛,咱們只要簡單地定義一個函數,它的返回值是一個類型謂詞:

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined; } 複製代碼

在這個例子裏,pet is Fish就是類型謂詞。 謂詞爲parameterName is Type這種形式,parameterName必須是來自於當前函數簽名裏的一個參數名。 每當使用一些變量調用isFish時,TypeScript會將變量縮減爲那個具體的類型,只要這個類型與變量的原始類型是兼容的。

// 'swim' 和 'fly' 調用都沒有問題了

if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}
複製代碼

十二、css-module的類型檢查

一開始繞了彎路,用typescript-plugin-css-modules,對less支持很差,當使用複雜less函數時會失效,在更新less文件時還常常須要重啓tsserver。 後來改進參考以下: typings-for-css-modules-loader

使用一個webpack loader,即時生成對應的.d.ts文件來映射css modulesclass

1三、tsc命令行參數

只編譯一個文件時,若是指定了tsc參數,則不會自動加載當前項目的tsconfig.json。這一點很迷惑,會生成錯誤的編譯結果。

1四、ts-loaderfork-ts-checker-webpack-pluginawesome-typescript-loader

ts-loader單獨使用,無命令行類型校驗提示,且項目越多編譯越慢。 ts-loader須要與fork-ts-checker-webpack-plugin一塊兒使用,能夠作到文件更新後秒編譯,在命令行顯示類型校驗,但消耗內存多。 awesome-typescript-loader消耗內存稍少,文件更新時編譯快,且能夠顯示命令行類型校驗。例如更改的是依賴較多的文件例如被view依賴的更底層的store,編譯速度會稍微慢。

1五、typescriptpreactweb component

首先須要在tsconfig.json中添加

"jsx": "react",
  "jsxFactory": "h",
複製代碼

在preact中使用自定義dom時,須要擴展IntrinsicElements,google上能搜到的地方都讓擴展global下的JSX

declare namespace JSX {
    interface IntrinsicElements {
        foo: any
    }
}
複製代碼

但在preact環境下JSX所在的命名空間不一樣

declare global {
  namespace preact {
    namespace h {
      namespace JSX {
        interface IntrinsicElements {
          'custom-node': any
        }
      }
    }
  }
}

複製代碼

配置

1六、tsconfig.json的路徑別名path

若是隻是當前項目使用,能夠盡情用

在和其餘項目一塊兒使用時,個人狀況是做爲多包的lerna項目,一個包須要引入另外一個包的ts文件,則原包中的路徑別名在另外一個包中是不能工做的。

1七、.d.ts與發佈

項目發佈時,即便打開了tsconfig.json中的declaration,其中的.d.ts文件也不會自動編譯到outDir指定的目錄中去

若是項目中有定義須要依賴這些.d.ts,須要在發佈過程當中添加額外的複製腳本

.d.ts中儘可能不要使用import引入其餘依賴,尤爲是不能引入非純定義文件的類型,好比該文件中含有實際的邏輯代碼,不然發佈後在使用該包的環境中不能正常工做

1八、使用babel編譯

因爲項目中有不少使用abstract定義的抽象類,在ts-loader編譯時抽象類只做爲類型檢查使用。但使用babel編譯時抽象類會生成真實的類,而且其中的屬性也都會與類一塊兒生成。在使用mobx擴展屬性時形成衝突致使沒法運行。

相關文章
相關標籤/搜索