網站搜尋

typescript2.0 - TypeScript 中 `declare` 關鍵字的用途


declare 關鍵字的意思是什麼?

type Callback = (err: Error | String, data: Array<CalledBackData>) => void;

對抗

declare type Callback = (err: Error | String, data:Array<CalledBackData>) => void;

找不到解釋 TS 中 declare 關鍵字用途的文件。這是什麼意思?

答案#1

簡要

declare 用來告訴編譯器「這個東西(通常是變數)已經存在,因此可以被其他程式碼引用,而且不需要將這個語句編譯成任何 JavaScript。」

常見使用情況:

你在你的網頁中新增一個對編譯器完全不知情的 JavaScript 文件的引用。也許它是來自其他域名的腳本,比如 'foo.com'。當這個腳本被評估時,它會創建一個帶有一些有用 API 方法的對象,並將其指派給全局作用域中的標識符 'fooSdk'。

你希望你的 TypeScript 代碼能夠調用 fooSdk.doSomething(),但由於你的編譯器不知道 fooSdk 變量存在,你將會得到一個編譯錯誤。

接著,你使用 declare 關鍵字作為告訴編譯器「相信我,這個變數存在且具有此類型」的一種方式。編譯器將使用這個聲明來靜態檢查其他代碼,但不會將其轉譯成輸出中的任何 JavaScript。

declare const fooSdk = { doSomething: () => boolean }

更新的 Typescript 版本需要稍微不同的語法:

declare const fooSdk : { doSomething: () => boolean }

同樣地,你可以在類別屬性上添加 declare 關鍵字,以告訴編譯器不要生成任何會創建此屬性的代碼,假設你有自己的代碼會創建它,而編譯器並不知道或無法理解這些代碼。

你的具體例子不同,因為你是在宣告一個類型,而不是變數,類型本身不會編譯成任何 JavaScript。我不知道宣告一個類型是否有任何理由。

答案#2

這是一個現實世界的例子。

我有一個使用 Webpack Hot Middleware 的 TypeScript React 應用程式。Webpack Hot Middleware 並不是用 TypeScript 編寫的,而是用傳統的 JavaScript 編寫的。所以它沒有 TypeScript 編譯器可以檢查的型別聲明。

當我運行我的程式碼時,來自 Webpack Hot Middleware 的對象 module 是存在的,儘管它是在我華麗的新 TypeScript React 應用中隱藏的老式 JavaScript,我仍然可以用 console.log 輸出它。

module 物件也有鍵,例如 module.hot,而這些鍵可以有值。但 TypeScript 設計時期的編譯器(至少在 VSCode 中)會在其下畫紅色波浪線,顯示 property 'hot' does not exist。但它確實存在!

要讓 TypeScript 編譯器同意,請這樣宣告:

declare let module: any

現有的 module 對象現在的類型是 any,這讓 TypeScript 編譯器滿意,紅色波浪線消失了,現在我可以繼續編譯並編寫其他代碼。

如果你移除關鍵字 declare,只寫 let module: any,它將無法編譯,反而會顯示 'module' 已經存在。這就是被接受答案中「ambient」的意思。

答案#3

來自 TypeScript 文件:

Typescript - 與其他 JavaScript 函式庫合作

要描述非 TypeScript 編寫的程式庫的形狀,我們需要 聲明 該程式庫所公開的 API。因為大多數 JavaScript 程式庫僅公開少數頂層對象,命名空間是一種表示它們的好方法。

我們將那些沒有定義實現的宣告稱為「環境」宣告。 通常這些會定義在 .d.ts 檔案中。如果你熟悉 C/C++,可以把它們當作 .h 檔案。讓我們來看幾個例子。

環境命名空間

熱門的圖書館 D3 在一個名為 d3 的全域物件中定義其功能。因為這個圖書館是透過一個 標籤(而不是模組加載器)來載入的,所以其聲明使用命名空間來定義它的形狀。為了讓 TypeScript 編譯器能看到這個形狀,我們使用了一個環境命名空間聲明。例如,我們可以這樣開始撰寫它:

D3.d.ts(簡化摘錄) :

declare namespace D3 {
    export interface Selectors {
        select: {
            (selector: string): Selection;
            (element: EventTarget): Selection;
        };
    }
    // (...)
}

答案#4

你可以使用 declare 在撰寫任何實作程式碼之前告訴編譯器類型,TypeScript 會很高興。

declare function foo(name: string): string

答案#5

考慮這個情況:你有一個 Typescript 專案。然後你因為某種原因決定用 JavaScript 寫一個模組,並在你的 Typescript 代碼中使用它。

// getMyName.js
module.exports = function getMyName(name) {
    return name;
}

然後你將它匯入到你的 TypeScript 代碼中

// myCode.ts
import getMyName from 'getMyName'; //getMyName is a JS module not typescript

當導入那個 JS 模組時(allowJs 編譯器選項應設為 false,否則 JS 文件將在沒有任何類型定義的情況下被解析),Typescript 的編譯器會抱怨那個模組根本不是模組!

Could not find a declaration file for module '/absolute/path/to/getMyName.ts'

這是因為 Typescript 自身不會解析成 JS 代碼。為了解決這個問題,我們應該為我們的 JS 模組提供類型定義(filename.d.ts)。為此,你可以使用 declare 和 export。

// getMyName.d.ts
declare function getMyName(name: string): string;

export default getMyName;

在那之後,當我們導入我們的 JS 模組時,TypeScript 不會抱怨任何事情,因為它現在可以訪問這些類型。