摘自中文网
类型
类型通过以下方式引入:
- 类型别名声明(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);
模块声明通配符: 某些模块加载器如 SystemJS
和 AMD
支持导入非 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;
}
}
最好的关于文件声明的资料
点击这里