TypeScript 中的聲明文件

參考:html

TypeScript學習筆記

TypeScript 中的聲明文件

tsconfig.json配置說明

【進階】TS高級類型,泛型

TSlint註釋忽略錯誤和RESTful理解

關閉Eslint語法校驗

詳解 ESLint 規則,規範你的代碼

學習 TypeScript 稍微有一段時間了,每次寫都會碰到有關聲明文件的問題,目前爲止暫未徹底搞清楚,在此記錄一些相關問題,之後碰到可以迅速解決。node

聲明文件(x.d.ts)

TypeScript 做爲 JavaScript 的超集,在開發過程當中不可避免要引用其餘第三方的 JavaScript 的庫。雖然經過直接引用能夠調用庫的類和方法,可是卻沒法使用TypeScript 諸如類型檢查等特性功能。爲了解決這個問題,須要將這些庫裏的函數和方法體去掉後只保留導出類型聲明,而產生了一個描述 JavaScript 庫和模塊信息的聲明文件。經過引用這個聲明文件,就能夠借用 TypeScript 的各類特性來使用庫文件了。react

在開始描述各類問題以前,列舉一下我所知道的聲明文件存放的方式(常規配置下):webpack

  1. src/@types/,在 src 目錄新建@types目錄,在其中編寫.d.ts聲明文件,聲明文件會自動被識別,能夠在此爲一些沒有聲明文件的模塊編寫本身的聲明文件; 2018-10-31:實際上在 tsconfig``include 字段包含的範圍內編寫 .d.ts,都將被自動識別。
  2. x.js相同目錄建立同名聲明文件x.d.ts,這樣也會被自動識別;
  3. node_modules/@types/下存放各個第三方模塊的聲明文件,經過yarn add @types/react自動下載到此處,本身編寫的聲明文件不要放在這裏;
  4. 做爲 npm 模塊發佈時,聲明文件可捆綁發佈,需在package.json中指明"types": "./types/index.d.ts"
  5. typings 聲明管理器,瞭解很少,已經不推薦使用;

隱式 any 類型(implicitly has an ‘any’ type)

當 tsconfig.json 中關閉"noImplicitAny": false時,能夠直接在 TypeScript 中引用 JavaScript(無聲明文件)的庫,全部的引入都會被默認爲any類型。但爲了規範編碼,老是打開"noImplicitAny": true,這樣當發生上述狀況時,編譯器會阻止編譯,提示咱們去加上類型規範。git

TS 中導入 JS

// hello.js
    exportconst hello = () =>console.log('hello')
    
    // index.ts
    import {hello} from'./hello'
    // 若是使用 vscode,編輯器就會給出錯誤提示:
    // [ts] 沒法找到模塊「./hello」的聲明文件。「src/hello.js」隱式擁有 "any" 類型。
    hello()
    
    // 若是執行編譯,控制檯也會給出一樣的錯誤提示:
    // Could not find a declaration file for module './hello'. 'src/hello.js' implicitly has an 'any' type.
複製代碼

這就告訴咱們,若要在index.ts中使用hello.js,須要先爲hello.js編寫聲明文件。github

關於 declareweb

// hello.d.ts
    // 描述 hello.js
    export declare const hello: () =>void
複製代碼

另外一種書寫方式,目前還沒徹底搞清二者本質區別:typescript

// hello.d.ts
    exportas namespace hello;
    
    export = hello;
    
    declare namespace hello {
        exportconst hello: () =>void;
    }
複製代碼

實際上,看了一些第三方模塊的聲明文件,形式也是五花八門,看得一頭霧水,學得一頭包……npm

TS 中導入 .png、.json 等

不止是在 TypeScript 中導入未聲明 JavaScript,導入.png.json等文件時也一樣須要去編寫聲明文件。json

提供一種方式,能夠建立一個聲明文件src/@types/definition.d.ts(你也能夠命名爲其餘),在其中編寫以下聲明:

// definition.d.ts
    declare module'*.png' {
        const value: string
        export = value
    }
    
    // index.ts
    // 以後在 TS 中導入也不會有問題
    import avatar from'./img/avatar.png'
複製代碼

或者可使用require

const avatar = require('./img/avatar.png')
    // 可能會提示 require 未定義,有兩種方式:
    //  1. 自行聲明:declare const require: any
    //  2. yarn add -D @types/node
複製代碼

第三方模塊沒有可用的聲明文件

通常使用第三方不是 TypeScript 編寫的模塊時,咱們能夠直接下載對應的聲明文件:yarn add @types/{模塊名}。然而有些模塊是沒有對應的聲明文件的,這時候就須要咱們本身編寫聲明文件,以rc-form爲例,只需在src/@types/definition.d.ts中添加對應代碼便可:

// definition.d.ts
    declare module'*.png' {
        const value: string
        export = value
    }
    
    // 新增部分
    declare module"rc-form" {
        // 在此只是簡單地進行類型描述
        exportconst createForm: any;
        exportconst createFormField: any;
        exportconst formShape: any;
    }
    
複製代碼

webpack 別名(aliases)

當 webpack 中配置了別名,在 TS 中使用時會出現找不到模塊

// webpack.config.js
    const config = {
         // ...
         aliases: {
         // 公共的工具類、容器和組件
         utils: path.resolve('../utils'),
      },
        // ...
    }
    
    // index.ts
    import {ua} from'utils/broswer'
    // Cannot find module 'utils/browser'
複製代碼

只需在 tsconfig.json 添加baseUrlpaths的配置便可:

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "noImplicitAny": true,
    // 添加配置
    "baseUrl": ".",
    "paths": {
      "utils/*": ["../utils/*"],
      "components/*": ["../components/*"]
    }
  },
  "include": ["./src/*", "./src/**/*"],
  "exclude": ["node_modules"]
}
複製代碼

類型「Window」上不存在屬性「X」

有時候,直接經過 script、src 引入的對象在 window.X 是能夠直接訪問的,但在 TS 中直接使用時會提示不存在相應屬性(The property ‘X’ does not exist on value of type ‘window’),這時候須要對 window 進行擴展,直接在src/@types/definition.d.ts中擴展。

// definition.d.ts
    interface Window {
      X: any
    }
    
    // index.ts
    console.log(window.X) // success
複製代碼

我在使用時,想複用一些類型,從其餘文件導入了一些內容,這時候出現了問題:

// definition.d.ts
    import {IPerson} from'./interfaces/index.ts'
    
    // ...
    
    interface Window {
      X: any
    }
    
    
    // index.ts
    console.log(window.X) // fail: 類型「Window」上不存在屬性「X」
複製代碼

而後發現,套一層global又能恢復正常,但沒有import語句時,使用declare global會提示錯誤:

// definition.d.ts
    import {IPerson} from'./interfaces/index.ts'
    
    // ...
    
    // global 包裹
    declare global {
      interface Window {
        X: any
      }
    }
    
    
    // index.ts
    console.log(window.X) // success
複製代碼

typescript 類型映射 (ReadOnly、Partial)

有時候須要一個類型,是依賴於上一個類型可是,對屬性的要求去不一樣

interface Person{
    name: string;
    agent: number;
}
type Person2 = Readonly<Person>;
type Person3 = Partial<Person>;
class Test {
    run() {
        let person: Person = {
            name: 'dd',
            agent: 1
        };
        person.name = 'cc';
        let person2: Person2 = {
            name: 'read',
            agent: 1
        };
        // person2.agent = 3; 報錯
        let person3: Person3 = {
            name: 'person 3' // 屬性不完整也不會報錯
        }
    }
}

複製代碼

ReadOnly、Partial源碼

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}
type Partial<T> = {
    [P in keyof T]?: T[P];
}
複製代碼

其實這些主要在定義是靈活應用 in, keyof便可實現

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
}
type Record<K extends string, T> = {
    [P in K]: T;
}
複製代碼
相關文章
相關標籤/搜索