node项目自动编译 各种技巧

在开发或调试Node.js应用程序的时候,当你修改js文件后,总是要按下CTRL+C终止程序,然后再重新启动,即使是修改一点小小的参数,也总是要不断地重复这几个很烦人的操作。有没有办法做到当文件修改之后,Node.js自动重新启动(或重新加载文件)以节省时间呢?一开始我是想到用grunt的watch模块来监控文件变化,但后来在网上一查,原来我们想到的,别人早已想到,并且已经做得很好。Node Supervisor正是这样一个可以实现这种需求的Node.js模块。

根据Github上的说明,Node Supervisor原本是用于服务器上Node.js应用崩溃的时候,自己重新启动。当然它也可以监控你的项目的js(或CoffeeScript)文件变化,进而重启来方便我们调试应用程序。

安装方法(以全局模块安装):

复制代码代码如下:

npm install supervisor -g

假设你的Node.js程序主入口是app.js,那么只需要执行以下命令,即可开始监控文件变化。

复制代码代码如下:

supervisor app.js

Supervisor还支持多种参数,列举如下:

复制代码代码如下:

//要监控的文件夹或js文件,默认为'.'
-w|--watch <watchItems>

//要忽略监控的文件夹或js文件  
-i|--ignore <ignoreItems>

//监控文件变化的时间间隔(周期),默认为Node.js内置的时间
-p|--poll-interval <milliseconds>

//要监控的文件扩展名,默认为'node|js'
-e|--extensions <extensions>

//要执行的主应用程序,默认为'node'
-x|--exec <executable>

//开启debug模式(用--debug flag来启动node)
--debug

//安静模式,不显示DEBUG信息
-q|--quiet

例子:

复制代码代码如下:

supervisor myapp.js
supervisor -w py_scripts -e 'py' -x python myapp.py
supervisor -w lib, server.js, config.js, server.js

实现同样功能的类似产品还有Run.jsNodeman,这两个我都没用过。但是从文档上来看,前者和Supervisor一样都是极简的5分钟就可以上手的那种,功能比Supervisor稍弱;后者的feature比较多,对应的文档就特别长,估计要研究透也得至少半个小时。选择哪一个,全看项目需求和个人喜好。


new 发布于 2017-2-8 09:20

电影推荐——持续更新 人生杂谈



2016-11-08

猎杀野蛮人——片子虽然题材不新颖,问题少年浪子回头,但是故事讲得轻松愉悦,有新意,没有大段的煽情。
时间规划局



new 发布于 2016-11-8 09:45

Sublime Text 3 Package Control安装 各种技巧

import urllib.request,os,hashlib; h = 'df21e130d211cfc94d9b0905775a7c0f' + '1e3d39e33b79698005270310898eea76'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by)

或手动安装:
下载文件:Package Control.sublime-package,置于Installed Packages/目录。
 
突然发现一个非常严重的问题,安装好Package Control之后却无法使用,坑坑坑。。。
解决方法:
    打开Preference/Settings-User ,发现     "ignored_packages": 

    [

Package Control

    ] Package Control竟然被ignore了。。。。 
删除即可。

new 发布于 2016-11-8 09:02

【转】Gradle和Maven 各种技巧

Java世界中主要有三大构建工具:Ant、Maven和Gradle。经过几年的发展,Ant几乎销声匿迹、Maven也日薄西山,而Gradle的发展则如日中天。笔者有幸见证了Maven的没落和Gradle的兴起。Maven的主要功能主要分为5点,分别是依赖管理系统、多模块构建、一致的项目结构、一致的构建模型和插件机制。我们可以从这五个方面来分析一下Gradle比起Maven的先进之处。

依赖管理系统

Maven为Java世界引入了一个新的依赖管理系统。在Java世界中,可以用groupId、artifactId、version组成的Coordination(坐标)唯一标识一个依赖。任何基于Maven构建的项目自身也必须定义这三项属性,生成的包可以是Jar包,也可以是war包或者ear包。一个典型的依赖引用如下所示:

1 2 3 4 5 6 7 8 9 10 
<dependency>  <groupId>junit</groupId>  <artifactId>junit</artifactId>  <version>4.12</version>  <scope>test</scope> </dependency> <dependency>  <groupId>org.springframework</groupId>  <artifactId>spring-test</artifactId> </dependency> 

从上面可以看出当引用一个依赖时,version可以省略掉,这样在获取依赖时会选择最新的版本。而存储这些组件的仓库有远程仓库和本地仓库之分。远程仓库可以使用世界公用的central仓库,也可以使用Apache Nexus自建私有仓库;本地仓库则在本地计算机上。通过Maven安装目录下的settings.xml文件可以配置本地仓库的路径,以及采用的远程仓库的地址。

Gradle在设计的时候基本沿用了Maven的这套依赖管理体系。不过它在引用依赖时还是进行了一些改进。首先引用依赖方面变得非常简洁。

1 2 3 4 
dependencies {  compile 'org.hibernate:hibernate-core:3.6.7.Final'  testCompile junit:junit:4.+' } 

第二,Maven和Gradle对依赖项的scope有所不同。在Maven世界中,一个依赖项有6种scope,分别是complie(默认)、provided、runtime、test、system、import。而grade将其简化为了4种,compile、runtime、testCompile、testRuntime。那么如果想在gradle使用类似于provided的scope怎么办?别着急,由于gradle语言的强大表现力,我们可以轻松编写代码来实现类似于provided scope的概念(例如How to use provided scope for jar file in Gradle build?)。

第三点是Gradle支持动态的版本依赖。在版本号后面使用+号的方式可以实现动态的版本管理。

第四点是在解决依赖冲突方面Gradle的实现机制更加明确。使用Maven和Gradle进行依赖管理时都采用的是传递性依赖;而如果多个依赖项指向同一个依赖项的不同版本时就会引起依赖冲突。而Maven处理这种依赖关系往往是噩梦一般的存在。而Gradle在解决依赖冲突方面相对来说比较明确。在Chapter 23. Dependency Management 中的23.2.3章节详细解读了gradle是如何处理版本冲突的。

多模块构建

在SOA和微服务的浪潮下,将一个项目分解为多个模块已经是很通用的一种方式。在Maven中需要定义个parent POM作为一组module的聚合POM。在该POM中可以使用<modules>标签来定义一组子模块。parent POM不会有什么实际构建产出。而parent POM中的build配置以及依赖配置都会自动继承给子module。

而Gradle也支持多模块构建。而在parent的build.gradle中可以使用allprojects和subprojects代码块来分别定义里面的配置是应用于所有项目还是子项目。对于子模块的定义是放置在setttings.gradle文件中的。在gradle的设计当中,每个模块都是Project的对象实例。而在parent build.gradle中通过allprojects或subprojects可以对这些对象进行各种操作。这无疑比Maven要灵活的多。

比如在parent的build.gradle中有以下代码:

1 2 3 
allprojects {  task hello << { task -> println "I'm $task.project.name" } } 

执行命令gradle -q hello会依次打印出父module以及各个submodule的项目名称。这种强大的能力能让gradle对各个模块具有更强的定制化。

一致的项目结构

在Ant时代大家创建Java项目目录时比较随意,然后通过Ant配置指定哪些属于source,那些属于testSource等。而Maven在设计之初的理念就是Conversion over configuration(约定大于配置)。其制定了一套项目目录结构作为标准的Java项目结构。一个典型的Maven项目结构如下:

Gradle也沿用了这一标准的目录结构。如果你在Gradle项目中使用了标准的Maven项目结构的话,那么在Gradle中也无需进行多余的配置,只需在文件中包含apply plugin:'java',系统会自动识别source、resource、test srouce、 test resource等相应资源。不过Gradle作为JVM上的构建工具,也同时支持groovy、scala等源代码的构建,甚至支持Java、groovy、scala语言的混合构建。虽然Maven通过一些插件(比如maven-scala-plugin)也能达到相同目的,但配置方面显然Gradle要更优雅一些。

一致的构建模型

为了解决Ant中对项目构建活动缺乏标准化的问题,Maven特意设置了标准的项目构建周期,其默认的构建周期如下所示:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 
<phases>  <phase>validate</phase>  <phase>initialize</phase>  <phase>generate-sources</phase>  <phase>process-sources</phase>  <phase>generate-resources</phase>  <phase>process-resources</phase>  <phase>compile</phase>  <phase>process-classes</phase>  <phase>generate-test-sources</phase>  <phase>process-test-sources</phase>  <phase>generate-test-resources</phase>  <phase>process-test-resources</phase>  <phase>test-compile</phase>  <phase>process-test-classes</phase>  <phase>test</phase>  <phase>prepare-package</phase>  <phase>package</phase>  <phase>pre-integration-test</phase>  <phase>integration-test</phase>  <phase>post-integration-test</phase>  <phase>verify</phase>  <phase>install</phase>  <phase>deploy</phase> </phases> 

而这种构建周期也是Maven最为人诟病的地方。因为Maven将项目的构建周期限制的太死,你无法在构建周期中添加新的phase,只能将插件绑定到已有的phase上。而现在项目的构建过程变得越来越复杂,而且多样化,显然Maven对这种复杂度缺少足够的应变能力。比如你想在项目构建过程中进行一项压缩所有javascript的任务,那么就要绑定到Maven的现有的某个phase上,而显然貌似放在哪个phase都不太合适。而且这些phase都是串行的,整个执行下来是一条线,这也限制了Maven的构建效率。而Gradle在构建模型上则非常灵活。在Gradle世界里可以轻松创建一个task,并随时通过depends语法建立与已有task的依赖关系。甚至对于Java项目的构建来说,Gradle是通过名为java的插件来包含了一个对Java项目的构建周期,这等于Gradle本身直接与项目构建周期是解耦的。

插件机制

Maven和Gradle设计时都采用了插件机制。但显然Gradle更胜一筹。主要原因在于Maven是基于XML进行配置。所以其配置语法太受限于XML。即使实现很小的功能都需要设计一个插件,建立其与XML配置的关联。比如想在Maven中执行一条shell命令,其配置如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 
<plugin>  <groupId>org.codehaus.mojo</groupId>  <artifactId>exec-maven-plugin</artifactId>  <version>1.2</version>  <executions>  <execution>  <id>drop DB => db_name</id>  <phase>pre-integration-test</phase>  <goals>  <goal>exec</goal>  </goals>  <configuration>  <executable>curl</executable>  <arguments>  <argument>-s</argument>  <argument>-S</argument>  <argument>-X</argument>  <argument>DELETE</argument>  <argument>http://${db.server}:${db.port}/db_name</argument>  </arguments>  </configuration>  </execution>  </executions> </plugin> 

而在Gradle中则一切变得非常简单。

1 2 3 
task dropDB(type: Exec) {  commandLine curl,-s,s,-x,DELETE,"http://${db.server}:{db.port}/db_name" } 

在创建自定义插件方面,Maven和Gradle的机制都差不多,都是继承自插件基类,然后实现要求的方法。这里就不展开说明。


从以上五个方面可以看出Maven和Gradle的主要差异。Maven的设计核心Convention Over Configuration被Gradle更加发扬光大,而Gradle的配置即代码又超越了Maven。在Gradle中任何配置都可以作为代码被执行的,我们也可以随时使用已有的Ant脚本(Ant task是Gradle中的一等公民)、Java类库、Groovy类库来辅助完成构建任务的编写。

这种采用本身语言实现的DSL对本身语言项目进行构建管理的例子比比皆是。比如Rake和Ruby、Grunt和JavaScript、Sbt和Ruby…..而Gradle之所以使用Groovy语言实现,是因为Groovy比Java语言更具表现力,其语法特性更丰富,又兼具函数式的特点。这几年兴起的语言(比如Scala、Go、Swift)都属于强类型的语言,兼具面向对象和函数式的特点。

最后想说的Gradle的命令行比Maven的要强大的多。以前写过一篇文章专门讲述了Gradle的命令行操作,详情请见Gradle命令行黑魔法


new 发布于 2016-11-3 07:02

看看H5游戏引擎 各种技巧

最近想学习下Html5游戏开发,了解了几个H5游戏引擎,Egret、Cocos2d-js,PharseJs、LayaAir,用哪一种的开发者都有,各有各的优势,各有各的坑。

于是初步看了Egret和Cocos-Js,Egret是用TS(typescript,微软出的一个语言)来开发,然后编译成js的,核心也是由js开发的,对于html5的支持很好,开发html5游戏效率和性能也是很高的;Cocos-Js的母体语言是C++,通过js binding实现H5,从性能上不如Egret,但它对于Native的支持则比Egret强。从开发的角度来看Cocos-js坑很多,很多问题需要修改底层源码,Egret的Bug也不少,开发轻量级的PC Html5游戏自然没有问题,只是对Native的效率还不够。



new 发布于 2016-11-1 06:01

swift开发之UIAlertController iOS应用开发

UIAlertController集合了AlertView和ActionSheet

Alert:


        

let msgBox:UIAlertController = UIAlertController(title: "Tips", message: "Select "+account_name_arr[indexPath.row], preferredStyle: UIAlertControllerStyle.Alert);
        msgBox.addAction(UIAlertAction(title: "OK", style: .Default, handler: {(action:UIAlertAction)->Void in
            print("->OK")
        }))
msgBox.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: {(action:UIAlertAction)->Void in        
        msgBox.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: {(action:UIAlertAction)->Void in
            print("->Cancel")
        }))
self.presentViewController(msgBox, animated: true, completion: nil)  



ActionSheet:


        

let choose_ac:UIAlertController = UIAlertController(title: "提示", message: "添加类型", preferredStyle: .ActionSheet)
        choose_ac.addAction(UIAlertAction(title: "账号", style: UIAlertActionStyle.Default, handler: {(action:UIAlertAction)->Void in
            print("->OK1")
            self.initData()
        }))
        choose_ac.addAction(UIAlertAction(title: "银行卡", style: UIAlertActionStyle.Default, handler: {(action:UIAlertAction)->Void in
            print("->OK2")
        }))
        choose_ac.addAction(UIAlertAction(title: "取消", style: UIAlertActionStyle.Cancel, handler: {(action:UIAlertAction)->Void in
            print("->OK2")
        }))
        self.presentViewController(choose_ac, animated: true, completion: nil)



标签: swift iOS

new 发布于 2016-10-20 06:24

[转]TypeScript中文文档——混入 TypeScripts

转自:https://zhongsp.gitbooks.io/typescript-handbook/content/

介绍

除了传统的面向对象继承方式,还流行一种通过可重用组件创建类的方式,就是联合另一个简单类的代码。 你可能在Scala等语言里对mixins及其特性已经很熟悉了,但它在JavaScript中也是很流行的。

混入示例

下面的代码演示了如何在TypeScript里使用混入。 后面我们还会解释这段代码是怎么工作的。

// Disposable Mixin class Disposable {
    isDisposed: boolean;
    dispose() { this.isDisposed = true;
    }

} // Activatable Mixin class Activatable {
    isActive: boolean;
    activate() { this.isActive = true;
    }
    deactivate() { this.isActive = false;
    }
} class SmartObject implements Disposable, Activatable { constructor() {
        setInterval(() => console.log(this.isActive + " : " + this.isDisposed), 500);
    }

    interact() { this.activate();
    } // Disposable isDisposed: boolean = false;
    dispose: () => void; // Activatable isActive: boolean = false;
    activate: () => void;
    deactivate: () => void;
}
applyMixins(SmartObject, [Disposable, Activatable]); let smartObj = new SmartObject();
setTimeout(() => smartObj.interact(), 1000); //////////////////////////////////////// // In your runtime library somewhere //////////////////////////////////////// function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        });
    });
} 

理解这个例子

代码里首先定义了两个类,它们将做为mixins。 可以看到每个类都只定义了一个特定的行为或功能。 稍后我们使用它们来创建一个新类,同时具有这两种功能。

// Disposable Mixin class Disposable {
    isDisposed: boolean;
    dispose() { this.isDisposed = true;
    }

} // Activatable Mixin class Activatable {
    isActive: boolean;
    activate() { this.isActive = true;
    }
    deactivate() { this.isActive = false;
    }
} 

下面创建一个类,结合了这两个mixins。 下面来看一下具体是怎么操作的:

class SmartObject implements Disposable, Activatable { 

首先应该注意到的是,没使用extends而是使用implements。 把类当成了接口,仅使用Disposable和Activatable的类型而非其实现。 这意味着我们需要在类里面实现接口。 但是这是我们在用mixin时想避免的。

我们可以这么做来达到目的,为将要mixin进来的属性方法创建出占位属性。 这告诉编译器这些成员在运行时是可用的。 这样就能使用mixin带来的便利,虽说需要提前定义一些占位属性。

// Disposable isDisposed: boolean = false;
dispose: () => void; // Activatable isActive: boolean = false;
activate: () => void;
deactivate: () => void; 

最后,把mixins混入定义的类,完成全部实现部分。

applyMixins(SmartObject, [Disposable, Activatable]); 

最后,创建这个帮助函数,帮我们做混入操作。 它会遍历mixins上的所有属性,并复制到目标上去,把之前的占位属性替换成真正的实现代码。

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => { Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        })
    });
}




new 发布于 2016-10-18 01:06

[转]TypeScript中文文档——三斜线命令 TypeScripts

转自:https://zhongsp.gitbooks.io/typescript-handbook/content/

三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。

三斜线指令可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。 如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。

/// <reference path="..." />

/// <reference path="..." />指令是三斜线指令中最常见的一种。 它用于声明文件间的依赖

三斜线引用告诉编译器在编译过程中要引入的额外的文件。

当使用--out--outFile时,它也可以做为调整输出内容顺序的一种方法。 文件在输出文件内容中的位置与经过预处理后的输入顺序一致。

预处理输入文件

编译器会对输入文件进行预处理来解析所有三斜线引用指令。 在这个过程中,额外的文件会加到编译过程中。

这个过程会以一些根文件开始; 它们是在命令行中指定的文件或是在tsconfig.json中的"files"列表里的文件。 这些根文件按指定的顺序进行预处理。 在一个文件被加入列表前,它包含的所有三斜线引用都要被处理,还有它们包含的目标。 三斜线引用以它们在文件里出现的顺序,使用深度优先的方式解析。

一个三斜线引用路径是相对于包含它的文件的,如果不是根文件。

错误

引用不存在的文件会报错。 一个文件用三斜线指令引用自己会报错。

使用 --noResolve

如果指定了--noResolve编译选项,三斜线引用会被忽略;它们不会增加新文件,也不会改变给定文件的顺序。

/// <reference no-default-lib="true"/>

这个指令把一个文件标记成默认库。 你会在lib.d.ts文件和它不同的变体的顶端看到这个注释。

这个指令告诉编译器在编译过程中不要包含这个默认库(比如,lib.d.ts)。 这与在命令行上使用--noLib相似。

还要注意,当传递了--skipDefaultLibCheck时,编译器只会忽略检查带有/// <reference no-default-lib="true"/>的文件。

/// <amd-module />

默认情况下生成的AMD模块都是匿名的。 但是,当一些工具需要处理生成的模块时会产生问题,比如r.js

amd-module指令允许给编译器传入一个可选的模块名:

amdModule.ts
///<amd-module name='NamedModule'/> export class C {
} 

这会将NamedModule传入到AMD define函数里:

amdModule.js
define("NamedModule", ["require", "exports"], function (require, exports) { var C = (function () { function C() {
        } return C;
    })();
    exports.C = C;
}); 

/// <amd-dependency />

注意:这个指令被废弃了。使用import "moduleName";语句代替。

/// <amd-dependency path="x" />告诉编译器有一个非TypeScript模块依赖需要被注入,做为目标模块require调用的一部分。

amd-dependency指令也可以带一个可选的name属性;它允许我们为amd-dependency传入一个可选名字:

/// <amd-dependency path="legacy/moduleA" name="moduleA"/> declare var moduleA:MyType
moduleA.callStuff() 

生成的JavaScript代码:

define(["require", "exports", "legacy/moduleA"], function (require, exports, moduleA) {
    moduleA.callStuff()
});




new 发布于 2016-10-18 01:06

[转]TypeScript中文文档——JSX TypeScripts

转自:https://zhongsp.gitbooks.io/typescript-handbook/content/



介绍

JSX是一种嵌入式的类似XML的语法。 它可以被转换成合法的JavaScript,尽管转换的语义是依据不同的实现而定的。 JSX因React框架而流行,但是也被其它应用所使用。 TypeScript支持内嵌,类型检查和将JSX直接编译为JavaScript。

基本用法

想要使用JSX必须做两件事:

  1. 给文件一个.tsx扩展名
  2. 启用jsx选项

TypeScript具有两种JSX模式:preservereact。 这些模式只在代码生成阶段起作用 - 类型检查并不受影响。 在preserve模式下生成代码中会保留JSX以供后续的转换操作使用(比如:Babel)。 另外,输出文件会带有.jsx扩展名。 react模式会生成React.createElement,在使用前不需要再进行转换操作了,输出文件的扩展名为.js

模式 输入 输出 输出文件扩展名
preserve <div /> <div /> .jsx
react <div /> React.createElement("div") .js

你可以通过在命令行里使用--jsx标记或tsconfig.json里的选项来指定模式。

注意:React标识符是写死的硬代码,所以你必须保证React(大写的R)是可用的。 Note: The identifier React is hard-coded, so you must make React available with an uppercase R.

as操作符

回想一下怎么写类型断言:

var foo = <foo>bar; 

这里我们断言bar变量是foo类型的。 因为TypeScript也使用尖括号来表示类型断言,JSX的语法带来了解析的困难。因此,TypeScript在.tsx文件里禁用了使用尖括号的类型断言。

为了弥补.tsx里的这个功能,新加入了一个类型断言符号:as。 上面的例子可以很容易地使用as操作符改写:

var foo = bar as foo; 

as操作符在.ts.tsx里都可用,并且与其它类型断言行为是等价的。

类型检查

为了理解JSX的类型检查,你必须首先理解固有元素与基于值的元素之间的区别。 假设有这样一个JSX表达式<expr />expr可能引用环境自带的某些东西(比如,在DOM环境里的divspan)或者是你自定义的组件。 这是非常重要的,原因有如下两点:

  1. 对于React,固有元素会生成字符串(React.createElement("div")),然而由你自定义的组件却不会生成(React.createElement(MyComponent))。
  2. 传入JSX元素里的属性类型的查找方式不同。 固有元素属性本身就支持,然而自定义的组件会自己去指定它们具有哪个属性。

TypeScript使用与React相同的规范 来区别它们。 固有元素总是以一个小写字母开头,基于值的元素总是以一个大写字母开头。

固有元素

固有元素使用特殊的接口JSX.IntrinsicElements来查找。 默认地,如果这个接口没有指定,会全部通过,不对固有元素进行类型检查。 然而,如果接口存在,那么固有元素的名字需要在JSX.IntrinsicElements接口的属性里查找。 例如:

declare namespace JSX { interface IntrinsicElements {
        foo: any }
}

<foo />; // 正确 <bar />; // 错误 

在上例中,<foo />没有问题,但是<bar />会报错,因为它没在JSX.IntrinsicElements里指定。

注意:你也可以在JSX.IntrinsicElements上指定一个用来捕获所有字符串索引:

declare namespace JSX { interface IntrinsicElements {
       [elemName: string]: any;
   }
} 

基于值的元素

基于值的元素会简单的在它所在的作用域里按标识符查找。

import MyComponent from "./myComponent";

<MyComponent />; // 正确 <SomeOtherComponent />; // 错误 

可以限制基于值的元素的类型。 然而,为了这么做我们需要引入两个新的术语:元素类的类型元素实例的类型

现在有<Expr />元素类的类型Expr的类型。 所以在上面的例子里,如果MyComponent是ES6的类,那么它的类类型就是这个类。 如果MyComponent是个工厂函数,类类型为这个函数。

一旦建立起了类类型,实例类型就确定了,为类类型调用签名的返回值与构造签名的联合类型。 再次说明,在ES6类的情况下,实例类型为这个类的实例的类型,并且如果是工厂函数,实例类型为这个函数返回值类型。

class MyComponent {
  render() {}
} // 使用构造签名 var myComponent = new MyComponent(); // 元素类的类型 => MyComponent // 元素实例的类型 => { render: () => void } function MyFactoryFunction() { return {
    render: () => {
    }
  }
} // 使用调用签名 var myComponent = MyFactoryFunction(); // 元素类的类型 => FactoryFunction // 元素实例的类型 => { render: () => void } 

元素的实例类型很有趣,因为它必须赋值给JSX.ElementClass或抛出一个错误。 默认的JSX.ElementClass{},但是它可以被扩展用来限制JSX的类型以符合相应的接口。

declare namespace JSX { interface ElementClass {
    render: any;
  }
} class MyComponent {
  render() {}
} function MyFactoryFunction() { return { render: () => {} }
}

<MyComponent />; // 正确 <MyFactoryFunction />; // 正确 class NotAValidComponent {} function NotAValidFactoryFunction() { return {};
}

<NotAValidComponent />; // 错误 <NotAValidFactoryFunction />; // 错误 

属性类型检查

属性类型检查的第一步是确定元素属性类型。 这在固有元素和基于值的元素之间稍有不同。

对于固有元素,这是JSX.IntrinsicElements属性的类型。

declare namespace JSX { interface IntrinsicElements {
    foo: { bar?: boolean }
  }
} // `foo`的元素属性类型为`{bar?: boolean}` <foo bar />; 

对于基于值的元素,就稍微复杂些。 它取决于先前确定的在元素实例类型上的某个属性的类型。 至于该使用哪个属性来确定类型取决于JSX.ElementAttributesProperty。 它应该使用单一的属性来定义。 这个属性名之后会被使用。

declare namespace JSX { interface ElementAttributesProperty {
    props; // 指定用来使用的属性名 }
} class MyComponent { // 在元素实例类型上指定属性 props: {
    foo?: string;
  }
} // `MyComponent`的元素属性类型为`{foo?: string}` <MyComponent foo="bar" /> 

元素属性类型用于的JSX里进行属性的类型检查。 支持可选属性和必须属性。

declare namespace JSX { interface IntrinsicElements {
    foo: { requiredProp: string; optionalProp?: number }
  }
}

<foo requiredProp="bar" />; // 正确 <foo requiredProp="bar" optionalProp={0} />; // 正确 <foo />; // 错误, 缺少 requiredProp <foo requiredProp={0} />; // 错误, requiredProp 应该是字符串 <foo requiredProp="bar" unknownProp />; // 错误, unknownProp 不存在 <foo requiredProp="bar" some-unknown-prop />; // 正确, `some-unknown-prop`不是个合法的标识符 

注意:如果一个属性名不是个合法的JS标识符(像data-*属性),并且它没出现在元素属性类型里时不会当做一个错误。

延展操作符也可以使用:

var props = { requiredProp: 'bar' };
; // 正确

var badProps = {};
; // 错误

JSX结果类型

默认地JSX表达式结果的类型为any。 你可以自定义这个类型,通过指定JSX.Element`接口。 然而,不能够从接口里检索元素,属性或JSX的子元素的类型信息。 它是一个黑盒。

嵌入的表达式

JSX允许你使用{ }标签来内嵌表达式。

var a =
{['foo', 'bar'].map(i => {i / 2})}

上面的代码产生一个错误,因为你不能用数字来除以一个字符串。 输出如下,若你使用了preserve选项:

var a =
{['foo', 'bar'].map(function (i) { return {i / 2}; })}

React整合

要想一起使用JSX和React,你应该使用React类型定义。 这些类型声明定义了JSX合适命名空间来使用React。

/// <reference path="react.d.ts" />

interface Props {
  foo: string;
}

class MyComponent extends React.Component<Props, {}> {
  render() {
    return <span>{this.props.foo}</span>
  }
}

<MyComponent foo="bar" />; // 正确
<MyComponent foo={0} />; // 错误



new 发布于 2016-10-18 00:54

[转]TypeScript中文文档——装饰器(Decorators) TypeScripts

转自:https://zhongsp.gitbooks.io/typescript-handbook/content/

介绍

随着TypeScript和ES6里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 Javascript里的装饰器目前处在建议征集的第一阶段,但在TypeScript里已做为一项实验性特性予以支持。

注意  装饰器是一项实验性特性,在未来的版本中可能会发生改变。

若要启用实验性的装饰器特性,你必须在命令行或tsconfig.json里启用experimentalDecorators编译器选项:

命令行:

tsc --target ES5 --experimentalDecorators 

tsconfig.json:

{ "compilerOptions": { "target": "ES5", "experimentalDecorators": true }
} 

装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明方法访问符属性参数上。 装饰器使用@expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

例如,有一个@sealed装饰器,我们会这样定义sealed函数:

function sealed(target) { // do something with "target" ... } 

注意  后面类装饰器小节里有一个更加详细的例子。

装饰器工厂

如果我们要定制一个修饰器如何应用到一个声明上,我们得写一个装饰器工厂函数。 装饰器工厂就是一个简单的函数,它返回一个表达式,以供装饰器在运行时调用。

我们可以通过下面的方式来写一个装饰器工厂函数:

function color(value: string) { // 这是一个装饰器工厂 return function (target) { //  这是装饰器 // do something with "target" and "value"... }
} 

注意  下面方法装饰器小节里有一个更加详细的例子。

装饰器组合

多个装饰器可以同时应用到一个声明上,就像下面的示例:

  • 书写在同一行上:
@f @g x 
  • 书写在多行上:
@f
@g
x 

当多个装饰器应用于一个声明上,它们求值方式与复合函数相似。在这个模型下,当复合fg时,复合的结果(f ∘ g)(x)等同于f(g(x))。

同样的,在TypeScript里,当多个装饰器应用在一个声明上时会进行如下步骤的操作:

  1. 由上至下依次对装饰器表达式求值。
  2. 求值的结果会被当作函数,由下至上依次调用。

如果我们使用装饰器工厂的话,可以通过下面的例子来观察它们求值的顺序:

function f() { console.log("f(): evaluated"); return function (target, propertyKey: string, descriptor: PropertyDescriptor) { console.log("f(): called");
    }
} function g() { console.log("g(): evaluated"); return function (target, propertyKey: string, descriptor: PropertyDescriptor) { console.log("g(): called");
    }
} class C {
    @f()
    @g()
    method() {}
} 

在控制台里会打印出如下结果:

f(): evaluated
g(): evaluated
g(): called
f(): called 

装饰器求值

类中不同声明上的装饰器将按以下规定的顺序应用:

1
  1. 参数装饰器,其次是方法访问符,或属性装饰器应用到每个实例成员。
  2. 参数装饰器,其次是方法访问符,或属性装饰器应用到每个静态成员。
  3. 参数装饰器应用到构造函数。
  4. 类装饰器应用到类。

类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。 类装饰器不能用在声明文件中(.d.ts),也不能用在任何外部上下文中(比如declare的类)。

类装饰器表达式会在运行时当作函数被调用,类的构造函数作为其唯一的参数。

如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。

注意  如果你要返回一个新的构造函数,你必须注意处理好原来的原型链。 在运行时的装饰器调用逻辑中不会为你做这些。

下面是使用类装饰器(@sealed)的例子,应用在Greeter类:

@sealed class Greeter {
    greeting: string; constructor(message: string) { this.greeting = message;
    }
    greet() { return "Hello, " + this.greeting;
    }
} 

我们可以这样定义@sealed装饰器:

function sealed(constructor: Function) { Object.seal(constructor);
    Object.seal(constructor.prototype);
} 

@sealed被执行的时候,它将密封此类的构造函数和原型。(注:参见Object.seal)

方法装饰器

方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的属性描述符上,可以用来监视,修改或者替换方法定义。 方法装饰器不能用在声明文件(.d.ts),重载或者任何外部上下文(比如declare的类)中。

方法装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 成员的属性描述符

注意  如果代码输出目标版本小于ES5Property Descriptor将会是undefined

如果方法装饰器返回一个值,它会被用作方法的属性描述符

注意  如果代码输出目标版本小于ES5返回值会被忽略。

下面是一个方法装饰器(@enumerable)的例子,应用于Greeter类的方法上:

class Greeter {
    greeting: string; constructor(message: string) { this.greeting = message;
    }

    @enumerable(false)
    greet() { return "Hello, " + this.greeting;
    }
} 

我们可以用下面的函数声明来定义@enumerable装饰器:

function enumerable(value: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.enumerable = value;
    };
} 

这里的@enumerable(false)是一个装饰器工厂。 当装饰器@enumerable(false)被调用时,它会修改属性描述符的enumerable属性。

访问器装饰器

访问器装饰器声明在一个访问器的声明之前(紧靠着访问器声明)。 访问器装饰器应用于访问器的属性描述符并且可以用来监视,修改或替换一个访问器的定义。 访问器装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如declare的类)里。

注意  TypeScript不允许同时装饰一个成员的getset访问器。取而代之的是,一个成员的所有装饰的必须应用在文档顺序的第一个访问器上。这是因为,在装饰器应用于一个属性描述符时,它联合了getset访问器,而不是分开声明的。

访问器装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 成员的属性描述符

注意  如果代码输出目标版本小于ES5Property Descriptor将会是undefined

如果访问器装饰器返回一个值,它会被用作方法的属性描述符

注意  如果代码输出目标版本小于ES5返回值会被忽略。

下面是使用了访问器装饰器(@configurable)的例子,应用于Point类的成员上:

class Point { private _x: number; private _y: number; constructor(x: number, y: number) { this._x = x; this._y = y;
    }

    @configurable(false) get x() { return this._x; }

    @configurable(false) get y() { return this._y; }
} 

我们可以通过如下函数声明来定义@configurable装饰器:

function configurable(value: boolean) { return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        descriptor.configurable = value;
    };
} 

属性装饰器

属性装饰器声明在一个属性声明之前(紧靠着属性声明)。 属性装饰器不能用在声明文件中(.d.ts),或者任何外部上下文(比如declare的类)里。

属性装饰器表达式会在运行时当作函数被调用,传入下列2个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。

注意  属性描述符不会做为参数传入属性装饰器,这与TypeScript是如何初始化属性装饰器的有关。 因为目前没有办法在定义一个原型对象的成员时描述一个实例属性,并且没办法监视或修改一个属性的初始化方法。 因此,属性描述符只能用来监视类中是否声明了某个名字的属性。

如果属性装饰器返回一个值,它会被用作方法的属性描述符

注意  如果代码输出目标版本小于ES5,返回值会被忽略。

如果访问符装饰器返回一个值,它会被用作方法的属性描述符

我们可以用它来记录这个属性的元数据,如下例所示:

class Greeter {
    @format("Hello, %s")
    greeting: string; constructor(message: string) { this.greeting = message;
    }
    greet() { let formatString = getFormat(this, "greeting"); return formatString.replace("%s", this.greeting);
    }
} 

然后定义@format装饰器和getFormat函数:

import "reflect-metadata"; const formatMetadataKey = Symbol("format"); function format(formatString: string) { return Reflect.metadata(formatMetadataKey, formatString);
} function getFormat(target: any, propertyKey: string) { return Reflect.getMetadata(formatMetadataKey, target, propertyKey);
} 

这个@format("Hello, %s")装饰器是个 装饰器工厂。 当@format("Hello, %s")被调用时,它添加一条这个属性的元数据,通过reflect-metadata库里的Reflect.metadata函数。 当getFormat被调用时,它读取格式的元数据。

注意  这个例子需要使用reflect-metadata库。 查看元数据了解reflect-metadata库更详细的信息。

参数装饰器

参数装饰器声明在一个参数声明之前(紧靠着参数声明)。 参数装饰器应用于类构造函数或方法声明。 参数装饰器不能用在声明文件(.d.ts),重载或其它外部上下文(比如declare的类)里。

参数装饰器表达式会在运行时当作函数被调用,传入下列3个参数:

  1. 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象。
  2. 成员的名字。
  3. 参数在函数参数列表中的索引。

注意  参数装饰器只能用来监视一个方法的参数是否被传入。

参数装饰器的返回值会被忽略。

下例定义了参数装饰器(@required)并应用于Greeter类方法的一个参数:

class Greeter {
    greeting: string; constructor(message: string) { this.greeting = message;
    }

    @validate
    greet(@required name: string) { return "Hello " + name + ", " + this.greeting;
    }
} 

然后我们使用下面的函数定义 @required 和 @validate 装饰器:

import "reflect-metadata"; const requiredMetadataKey = Symbol("required"); function required(target: Object, propertyKey: string | symbol, parameterIndex: number) { let existingRequiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];
    existingRequiredParameters.push(parameterIndex);
    Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);
} function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<Function>) { let method = descriptor.value;
    descriptor.value = function () { let requiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName); if (requiredParameters) { for (let parameterIndex of requiredParameters) { if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined) { throw new Error("Missing required argument.");
                }
            }
        } return method.apply(this, arguments);
    }
} 

@required装饰器添加了元数据实体把参数标记为必需的。 @validate装饰器把greet方法包裹在一个函数里在调用原先的函数前验证函数参数。

注意  这个例子使用了reflect-metadata库。 查看元数据了解reflect-metadata库的更多信息。

元数据

一些例子使用了reflect-metadata库来支持实验性的metadata API。 这个库还不是ECMAScript (JavaScript)标准的一部分。 然而,当装饰器被ECMAScript官方标准采纳后,这些扩展也将被推荐给ECMAScript以采纳。

你可以通过npm安装这个库:

npm i reflect-metadata --save 

TypeScript支持为带有装饰器的声明生成元数据。 你需要在命令行或tsconfig.json里启用emitDecoratorMetadata编译器选项。

Command Line:

tsc --target ES5 --experimentalDecorators --emitDecoratorMetadata 

tsconfig.json:

{ "compilerOptions": { "target": "ES5", "experimentalDecorators": true, "emitDecoratorMetadata": true }
} 

当启用后,只要reflect-metadata库被引入了,设计阶段添加的类型信息可以在运行时使用。

如下例所示:

import "reflect-metadata"; class Point {
    x: number;
    y: number;
} class Line { private _p0: Point; private _p1: Point;

    @validate set p0(value: Point) { this._p0 = value; } get p0() { return this._p0; }

    @validate set p1(value: Point) { this._p1 = value; } get p1() { return this._p1; }
} function validate<T>(target: any, propertyKey: string, descriptor: TypedPropertyDescriptor<T>) { let set = descriptor.set;
    descriptor.set = function (value: T) { let type = Reflect.getMetadata("design:type", target, propertyKey); if (!(value instanceof type)) { throw new TypeError("Invalid type.");
        }
    }
} 

TypeScript编译器可以通过@Reflect.metadata装饰器注入设计阶段的类型信息。 你可以认为它相当于下面的TypeScript:

class Line { private _p0: Point; private _p1: Point;

    @validate
    @Reflect.metadata("design:type", Point) set p0(value: Point) { this._p0 = value; } get p0() { return this._p0; }

    @validate
    @Reflect.metadata("design:type", Point) set p1(value: Point) { this._p1 = value; } get p1() { return this._p1; }
} 

注意  装饰器元数据是个实验性的特性并且可能在以后的版本中发生破坏性的改变(breaking changes)。




new 发布于 2016-10-18 00:54