关于类型声明文件 - 02核心概念

摘自中文网

类型

类型通过以下方式引入:

  • 类型别名声明(type sn = number | string;)
  • 接口声明(interface I { x: number[]; })
  • 类声明(class C { })
  • 枚举声明(enum E { A, B, C })
  • 指向某个类型的import声明

以上每种声明形式都会创建一个新的类型名称。

与类型相比,你可能已经理解了什么是值。 值是运行时名字,可以在表达式里引用。 比如 let x = 5;创建一个名为x的值。

同样,以下方式能够创建值:

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

命名空间

类型可以存在于命名空间里。 比如,有这样的声明 let x: A.B.C, 我们就认为 C类型来自A.B命名空间。

由上面类型/值的创建方式可知, 命名空间是属于创建值的方式,而不是类型的创建方式

简单的组合:一个名字,多种意义

一个给定的名字A,我们可以找出三种不同的意义:一个类型,一个值或一个命名空间。 要如何去解析这个名字要看它所在的上下文是怎样的。 比如,在声明 let m: A.A = A;, A首先被当做命名空间,然后做为类型名,最后是值。 这些意义最终可能会指向完全不同的声明!

内置组合

眼尖的读者可能会注意到,比如,class同时出现在类型和值列表里。 class C { }声明创建了两个东西: 类型C指向类的实例结构, 值C指向类构造函数。 枚举声明拥有相似的行为。

用户组合

假设我们写了模块文件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很相关 因此我们想让他们有相同的名字。 我们可以使用组合通过相同的名字 Bar表示这两种不同的对象(值和对象):

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

这提供了解构使用的机会:

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

再次地,这里我们使用Bar做为类型和值。 注意我们没有声明 Bar值为Bar类型 -- 它们是独立的。

declare的使用

.d.ts文件中使用declare 来声明变量的类型, 能用在全局命名空间(全局声明)或者包声明文件(声明一个局部变量)中, 这个声明仅仅用于编译时的检查,在编译结果中会被删除.

声明全局变量

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

声明全局函数

declare function greet(greeting: string): void;

declare namespace 描述用点表示法访问的类型或值(对象)

注意 namespace 内代码的写法和在全局变量下是一样的, 也是写 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 时, 声明相同的模块名(插件是为了增强这个模块)

/*~ 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 无法识别的资源时,就需要告诉ts,怎么识别这些导入的资源的类型。

// 看看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 声明模块之三

和上文"之二"有类似效果, 可以认为都是"模块补充"

// 声明合并效果
// vue的声明在 vue/types/vue.d.ts
import Vue from 'vue'
declare module 'vue/types/vue' {
    // 相当于Vue.$eventBus
    interface Vue { 
        $eventBus: Vue;
    }
    // 相当于在Vue.prototype.$eventBus  即全局属性
    interface VueConstructor {
        $eventBus: Vue;
    }
}

// 声明vue中额外的组件选项
// ComponentOptions 声明于 types/options.d.ts 之中
declare module 'vue/types/options' {
  interface ComponentOptions {
    myOption?: string
  }
}

declare module 声明模块之四

用于外部模块的统一声明, 即把所有模块的声明写到一个 .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;
}

外部模块简写: 假如你不想在使用一个新模块之前花时间去编写声明,你可以采用声明的简写形式以便能够快速使用它。

// declarations.d.ts
// 简写模块里所有导出的类型将是any
declare module "hot-new-module";

//---------
// ts文件中引入模块时
import x, {y} from "hot-new-module";
x(y);

模块声明通配符: 某些模块加载器如 SystemJSAMD支持导入非 JavaScript内容。 它们通常会使用一个前缀或后缀来表示特殊的加载语法。 模块声明通配符可以用来表示这些情况。

// 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;
}

// ---------
//现在你可以就导入匹配"*!text"或"json!*"的内容了。
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时:

  • 可以通过 declare 声明
  • 书写 namespacke 内部的代码时和写全局命名空间一样.例如可以写 export, var 等, 而不是因为命名空间后面有 {} 就认为是对象(在非 .d.ts 文件内可以认为是对象)
// 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;
    }
}

最好的关于文件声明的资料

点击这里

你可能感兴趣的:(关于类型声明文件 - 02核心概念)