類型
類型通過以下方式引入:
- 類型別名聲明(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);
模塊聲明通配符: 某些模塊加載器如 SystemJS 和 AMD支持導(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;
}
}