關(guān)于類型聲明文件 - 02核心概念

摘自中文網(wǎng)

類型

類型通過以下方式引入:

  • 類型別名聲明(type sn = number | string;)
  • 接口聲明(interface I { x: number[]; })
  • 類聲明(class C { })
  • 枚舉聲明(enum E { A, B, C })
  • 指向某個(gè)類型的import聲明

以上每種聲明形式都會創(chuàng)建一個(gè)新的類型名稱。

與類型相比,你可能已經(jīng)理解了什么是值。 值是運(yùn)行時(shí)名字,可以在表達(dá)式里引用。 比如 let x = 5;創(chuàng)建一個(gè)名為x的值。

同樣,以下方式能夠創(chuàng)建值:

  • let,const,和var聲明
  • 包含值的namespace或module聲明
  • enum聲明
  • class聲明
  • 指向值的import聲明
  • function聲明

命名空間

類型可以存在于命名空間里。 比如,有這樣的聲明 let x: A.B.C, 我們就認(rèn)為 C類型來自A.B命名空間。

由上面類型/值的創(chuàng)建方式可知, 命名空間是屬于創(chuàng)建值的方式,而不是類型的創(chuàng)建方式

簡單的組合:一個(gè)名字,多種意義

一個(gè)給定的名字A,我們可以找出三種不同的意義:一個(gè)類型,一個(gè)值或一個(gè)命名空間。 要如何去解析這個(gè)名字要看它所在的上下文是怎樣的。 比如,在聲明 let m: A.A = A;, A首先被當(dāng)做命名空間,然后做為類型名,最后是值。 這些意義最終可能會指向完全不同的聲明!

內(nèi)置組合

眼尖的讀者可能會注意到,比如,class同時(shí)出現(xiàn)在類型和值列表里。 class C { }聲明創(chuàng)建了兩個(gè)東西: 類型C指向類的實(shí)例結(jié)構(gòu), 值C指向類構(gòu)造函數(shù)。 枚舉聲明擁有相似的行為。

用戶組合

假設(shè)我們寫了模塊文件foo.d.ts:

export var SomeVar: { a: SomeType };
export interface SomeType {
  count: number;
}

這樣使用它:

import * as foo from './foo';
let x: foo.SomeType = foo.SomeVar.a;
console.log(x.count);
這可以很好地工作,但是我們知道SomeType和SomeVar很相關(guān) 因此我們想讓他們有相同的名字。 我們可以使用組合通過相同的名字 Bar表示這兩種不同的對象(值和對象):

export var Bar: { a: Bar };
export interface Bar {
  count: number;
}

這提供了解構(gòu)使用的機(jī)會:

import { Bar } from './foo';
let x: Bar = Bar.a;
console.log(x.count);

再次地,這里我們使用Bar做為類型和值。 注意我們沒有聲明 Bar值為Bar類型 -- 它們是獨(dú)立的。

declare的使用

.d.ts文件中使用declare 來聲明變量的類型, 能用在全局命名空間(全局聲明)或者包聲明文件(聲明一個(gè)局部變量)中, 這個(gè)聲明僅僅用于編譯時(shí)的檢查,在編譯結(jié)果中會被刪除.

聲明全局變量

declare var foo: number;
declare const foo: number;
declare let foo: number;

聲明全局函數(shù)

declare function greet(greeting: string): void;

declare namespace 描述用點(diǎn)表示法訪問的類型或值(對象)

注意 namespace 內(nèi)代碼的寫法和在全局變量下是一樣的, 也是寫 function, let

declare namespace myLib {
    function makeGreeting(s: string): string;
    let numberOfGreetings: number;
}

// 代碼中使用
let result = myLib.makeGreeting("hello, world");
console.log("The computed greeting is:" + result);

let count = myLib.numberOfGreetings;

declare module 聲明模塊之一

在書寫模塊插件.d.ts 時(shí), 聲明相同的模塊名(插件是為了增強(qiáng)這個(gè)模塊)

/*~ On this line, import the module which this module adds to */
import * as m from 'someModule';

/*~ You can also import other modules if needed */
import * as other from 'anotherModule';
/*~ Here, declare the same module as the one you imported above */
declare module 'someModule' {
    /*~ Inside, add new function, classes, or variables. You can use
     *~ unexported types from the original module if needed. */
    export function theNewMethod(x: m.foo): other.bar;

    /*~ You can also add new properties to existing interfaces from
     *~ the original module by writing interface augmentations */
    export interface SomeModuleOptions {
        someModuleSetting?: string;
    }

    /*~ New types can also be declared and will appear as if they
     *~ are in the original module */
    export interface MyModulePluginOptions {
        size: number;
    }
}

declare module 聲明模塊之二

在前端工程中,import 很多非 js 資源,例如:css, html, 圖片,vue, 這種 ts 無法識別的資源時(shí),就需要告訴ts,怎么識別這些導(dǎo)入的資源的類型。

// 看看vue怎么處理的:shims-vue.d.ts
declare module '*.vue' {
 import Vue from 'vue';
 export default Vue;
}
 
// html
declare module '*.html';
// css
declare module '*.css';

declare module 聲明模塊之三

和上文"之二"有類似效果, 可以認(rèn)為都是"模塊補(bǔ)充"

// 聲明合并效果
// vue的聲明在 vue/types/vue.d.ts
import Vue from 'vue'
declare module 'vue/types/vue' {
    // 相當(dāng)于Vue.$eventBus
    interface Vue { 
        $eventBus: Vue;
    }
    // 相當(dāng)于在Vue.prototype.$eventBus  即全局屬性
    interface VueConstructor {
        $eventBus: Vue;
    }
}

// 聲明vue中額外的組件選項(xiàng)
// ComponentOptions 聲明于 types/options.d.ts 之中
declare module 'vue/types/options' {
  interface ComponentOptions<V extends Vue> {
    myOption?: string
  }
}

declare module 聲明模塊之四

用于外部模塊的統(tǒng)一聲明, 即把所有模塊的聲明寫到一個(gè) .d.ts 文件中(理解見上文".d.ts文件的理解")

// node.d.ts
declare module "url" {
    export interface Url {
        protocol?: string;
        hostname?: string;
        pathname?: string;
    }

    export function parse(urlStr: string, parseQueryString?, slashesDenoteHost?): Url;
}

declare module "path" {
    export function normalize(p: string): string;
    export function join(...paths: any[]): string;
    export let sep: string;
}

外部模塊簡寫: 假如你不想在使用一個(gè)新模塊之前花時(shí)間去編寫聲明,你可以采用聲明的簡寫形式以便能夠快速使用它。

// declarations.d.ts
// 簡寫模塊里所有導(dǎo)出的類型將是any
declare module "hot-new-module";

//---------
// ts文件中引入模塊時(shí)
import x, {y} from "hot-new-module";
x(y);

模塊聲明通配符: 某些模塊加載器如 SystemJSAMD支持導(dǎo)入非 JavaScript內(nèi)容。 它們通常會使用一個(gè)前綴或后綴來表示特殊的加載語法。 模塊聲明通配符可以用來表示這些情況。

// xxx.d.ts
declare module "*!text" {
    const content: string;
    export default content;
}
// Some do it the other way around.
declare module "json!*" {
    const value: any;
    export default value;
}

// ---------
//現(xiàn)在你可以就導(dǎo)入匹配"*!text"或"json!*"的內(nèi)容了。
import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
console.log(data, fileContent);

注意: 沒有 declare interface 的寫法, 需要聲明接口直接寫 interface,或者在命名空間中 export interface 即可!

理解 namespace

命名空間: 作為全局命名空間的子空間存在. 在書寫 .d.ts時(shí):

  • 可以通過 declare 聲明
  • 書寫 namespacke 內(nèi)部的代碼時(shí)和寫全局命名空間一樣.例如可以寫 export, var 等, 而不是因?yàn)槊臻g后面有 {} 就認(rèn)為是對象(在非 .d.ts 文件內(nèi)可以認(rèn)為是對象)
// module-class.d.ts 類模塊的聲明文件

export = MyClass;

/*~ Write your module's methods and properties in this class */
declare class MyClass {
    constructor(someParam?: string);

    someProperty: string[];

    myMethod(opts: MyClass.MyClassMethodOptions): number;
}

/*~ If you want to expose types from your module as well, you can
 *~ place them in this block.
 */
declare namespace MyClass {
    export interface MyClassMethodOptions {
        width?: number;
        height?: number;
    }
}

最好的關(guān)于文件聲明的資料

點(diǎn)擊這里

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容