TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准。
TypeScript 由微软开发的自由和开源的编程语言。
TypeScript 设计目标是开发大型应用,它可以编译成纯 JavaScript,编译出来的 JavaScript 可以运行在任何浏览器上。
TypeScript 是一种给 JavaScript 添加特性的语言扩展,新增的功能包括:
从 ECMA 2015 反向移植而来的功能:
JavaScript与TypeScript的区别:
TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。
TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。
TypeScript官网:https://www.typescriptlang.org/
TypeScript中文官网:TypeScript中文网 · TypeScript——JavaScript的超集
我们需要使用到 npm 工具安装,npm是随同NodeJS一起安装的包管理工具,如果没有安装npm,直接安装nodejs就可以了,nodejs安装包下载 https://nodejs.org/en/download
cmd 执行npm -v 命令查看版本号
安装国内镜像
npm config set registry https://registry.npmmirror.com
安装TypeScript
npm install -g typescript
tsc -v 查看安装的版本号
开发工具使用 Visual Studio Code,下载地址Visual Studio Code - Code Editing. Redefined
TypeScript组成:
第一个TypeScript程序
创建myTS.ts文件,在vsCode中写入代码
const hello:string ="Hello MyWorld!"
console.log(hello);
打开命令终端,cd命令进入ts文件目录,执行以下命令
1)tsc myTS.ts 命令将ts文件编译成js文件
2)node myTS.js 运行js文件
编译参数 |
说明 |
--help |
显示帮助信息 |
--module |
载入扩展模块 |
--target |
设置 ECMA 版本 |
--declaration |
额外生成一个 .d.ts 扩展名的文件。 tsc ts-hw.ts --declaration 以上命令会生成 ts-hw.d.ts、ts-hw.js 两个文件。 |
--removeComments |
删除文件的注释 |
--out |
编译多个文件并合并到一个输出的文件 |
--sourcemap |
生成一个 sourcemap (.map) 文件。sourcemap 是一个存储源代码与编译代码对应位置映射的信息文件。 |
--module noImplicitAny |
在表达式和声明上有隐含的 any 类型时报错 |
--watch |
在监视模式下运行编译器。会监视输出文件,在它们改变时重新编译。 |
break |
as |
catch |
switch |
case |
if |
throw |
else |
var |
number |
string |
get |
module |
type |
instanceof |
typeof |
public |
private |
enum |
export |
finally |
for |
while |
void |
null |
super |
this |
new |
in |
return |
true |
false |
any |
extends |
static |
let |
package |
implements |
interface |
function |
do |
try |
yield |
const |
continue |
以下代码都是合法的:
console.log("Runoob")
console.log("Google");
如果语句写在同一行则一定需要使用分号来分隔,否则会报错,如:
console.log("MyTS");console.log("Google");//不报错
console.log("MyTS") console.log("Google");//报错
数据类型速览表
数据类型 |
关键字 |
描述 |
任意类型 |
any |
声明为 any 的变量可以赋予任意类型的值。 |
数字类型 |
number |
双精度 64 位浮点值。它可以用来表示整数和分数。 |
字符串类型 |
string |
一个字符系列,使用单引号(')或双引号(")来表示字符串类型。反引号(`)来定义多行文本和内嵌表达式。 |
布尔类型 |
boolean |
表示逻辑值:true 和 false。 |
数组类型 |
无 |
声明变量为数组。 |
元组 |
无 |
元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同。 |
枚举 |
enum |
枚举类型用于定义数值集合。 |
void |
void |
用于标识方法返回值的类型,表示该方法没有返回值。只能给他赋予undefined和null: let unusable: void = undefined; |
null |
null |
表示对象值缺失。 |
undefined |
undefined |
用于初始化变量为一个未定义的值 |
never |
never |
never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。 |
任意值是 TypeScript 针对编程时类型不明确的变量使用的一种数据类型,它常用于以下三种情况:
/*any 变量的值会动态改变时,比如来自用户的输入,任意值类型可以让这些变量跳过编译阶段的类型检查 */
let x:any=1;
console.log(x);//输出结果 1
x="hello any";
console.log(x);//输出结果 hello any
x=false;
console.log(x);//输出结果 false
/**any 改写现有代码时,任意值允许在编译时可选择地包含或移除类型检查 */
let y: any = 4;
y.ifItExists(); // 正确,ifItExists方法在运行时可能存在,但这里并不会检查
y.toFixed(); // 正确
let prettySure:Object=4;
prettySure.toFixed(); // 错误 Property 'toFixed' does not exist on type 'Object'.
/**any 定义存储各种类型数据的数组时 */
let arrayList: any[] = [1, false, 'fine'];
arrayList[1] = 100;
console.log(arrayList); //输出结果 [ 1, 100, 'fine' ]
和JavaScript一样,TypeScript里的所有数字都是浮点数。 这些浮点数的类型是 number
。 除了支持十进制和十六进制字面量,Typescript还支持ECMAScript 2015中引入的二进制和八进制字面量。
let decLiteral: number = 6;//十进制
console.log(decLiteral);//输出结果 6
let hexLiteral: number = 0xf00d;//十六进制
console.log(decLiteral);//输出结果 6
let binaryLiteral: number = 0b1010;//二进制
console.log(decLiteral);//输出结果 6
let octalLiteral: number = 0o744;//八进制
console.log(decLiteral);//输出结果 6
Number 对象是原始数值的包装对象
语法:var num=new Number(value);
注意: 如果一个参数值不能转换为一个数字将返回 NaN (非数字值)
Number对象属性
属性 |
描述 |
MAX_VALUE |
可表示的最大的数,MAX_VALUE 属性值接近于 1.79E+308。大于 MAX_VALUE 的值代表 "Infinity"。 |
MIN_VALUE |
可表示的最小的数,即最接近 0 的正数 (实际上不会变成 0)。最大的负数是 -MIN_VALUE,MIN_VALUE 的值约为 5e-324。小于 MIN_VALUE ("underflow values") 的值将会转换为 0。 |
NaN |
非数字值(Not-A-Number)。 |
NEGATIVE_INFINITY |
负无穷大,溢出时返回该值。该值小于 MIN_VALUE。 |
POSITIVE_INFINITY |
正无穷大,溢出时返回该值。该值大于 MAX_VALUE。 |
prototype |
Number 对象的静态属性。使您有能力向对象添加属性和方法。 |
constructor |
返回对创建此对象的 Number 函数的引用。 |
var num=new Number(20);
console.log(num);// 输出结果 [Number: 20]
var strToNum=new Number("30");
console.log(strToNum);//输出结果 [Number: 30]
var enToNum=new Number("abcs");
console.log(enToNum);//输出结果 [Number: NaN]
//prototype 属性
function employee(id:number,name:string) {
this.id = id;
this.name = name;
}
var emp = new employee(123,"admin");
employee.prototype.email = "[email protected]";
console.log("员工号: "+emp.id);
console.log("员工姓名: "+emp.name);
console.log("员工邮箱: "+emp.email);
Number对象方法
方法 |
描述 |
toExponential() |
把对象的值转换为指数计数法。 |
toFixed() |
把数字转换为字符串,并对小数点指定位数。 |
toLocaleString() |
把数字转换为字符串,使用本地数字格式顺序。 |
toPrecision() |
把数字格式化为指定的长度。 |
toString() |
把数字转换为字符串,使用指定的基数。数字的基数是 2 ~ 36 之间的整数。若省略该参数,则使用基数 10。 |
valueOf() |
返回一个 Number 对象的原始数字值。 |
var num=new Number(20.1314);
console.log(`toExponential转为指数计数法:${num.toExponential()}`);// 输出结果 2.01314e+1
console.log(`toFixed不保留小数:${num.toFixed()}`);//输出结果 20
console.log(`toFixed保留2位小数:${num.toFixed(2)}`);//输出结果20.3
console.log(`toFixed保留五位小数:${num.toFixed(5)}`);//输出结果 20.13140
console.log(`toLocaleString转成字符串:${num.toLocaleString()}`);//输出结果 20.1314
console.log(`toPrecision数字格式化指定长度:${num.toPrecision()}`);// 输出结果 20.1314
console.log(`toPrecision数字格式化指定1位长度:${num.toPrecision(1)}`);//输出结果 2e+1
console.log(`toPrecision数字格式化指定2位长度:${num.toPrecision(2)}`);//输出结果 20
console.log(`toPrecision数字格式化指定3位长度:${num.toPrecision(3)}`);//输出结果 20.1
console.log(`toString转换为字符换:${num.toString()}`);// 输出结果 20.1314
console.log(`toString转换为二进制字符换:${num.toString(2)}`);// 输出结果 10100.0010000110100011011011100010111010110001110001
console.log(`toString转换为八进制字符换:${num.toString(8)}`);// 输出结果 24.1032155613530704
console.log(`toString转换为十六进制字符换:${num.toString(16)}`);// 输出结果 14.21a36e2eb1c4
console.log(`valueOf原始数字值:${num.valueOf()}`);//输出结果 20.1314
let myname: string = "TypeScript";//双引号
let mySimpleName:string='TS';//单引号
let years: number = 5;
let words: string = `您好,今年是${ myname }(简称${mySimpleName})发布${ years + 1}周年`;//模板字符串
console.log(words);//输出结果 您好,今年是TypeScript(简称TS)发布6周年
let fullTxt:string='您好,今年是'+myname+'(简称'+mySimpleName+')发布'+(years+1)+'周年';//拼接字符串
console.log(fullTxt);//输出结果 您好,今年是TypeScript(简称TS)发布6周年
String 对象用于处理文本(字符串)
语法:var txt=new String("string");
String对象属性
属性 |
描述 |
constructor |
对创建该对象的函数的引用 |
length |
返回字符串的长度 |
prototype |
允许您向对象添加属性和方法 |
var str = new String( "This is string" ); // 等同于 var str="This is string";
console.log(`str.constructor is:${str.constructor}`);
console.log(`字符串长度:${str.length}`);
//prototype
function employee(id:number,name:string) {
this.id = id;
this.name = name;
}
var emp = new employee(123,"admin");
employee.prototype.email="[email protected]"; // 添加属性 email
console.log("员工号: "+emp.id);
console.log("员工姓名: "+emp.name);
console.log("员工邮箱: "+emp.email);
String对象方法
方法 |
描述 |
charAt() |
返回在指定位置的字符。 |
charCodeAt() |
返回在指定的位置的字符的 Unicode 编码。 |
concat() |
连接两个或更多字符串,并返回新的字符串。 |
indexOf() |
返回某个指定的字符串值在字符串中首次出现的位置。 |
lastIndexOf() |
从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置。 |
localeCompare() |
用本地特定的顺序来比较两个字符串。 |
match() |
查找找到一个或多个正则表达式的匹配。 |
replace() |
替换与正则表达式匹配的子串 |
search() |
检索与正则表达式相匹配的值 |
split() |
把字符串分割为子字符串数组。 |
substring() |
提取字符串中两个指定的索引号之间的字符。 |
toLocaleLowerCase() |
根据主机的语言环境把字符串转换为小写,只有几种语言(如土耳其语)具有地方特有的大小写映射。 |
toLocaleUpperCase() |
据主机的语言环境把字符串转换为大写,只有几种语言(如土耳其语)具有地方特有的大小写映射。 |
toLowerCase() |
把字符串转换为小写。 |
toString() |
返回字符串。 |
toUpperCase() |
把字符串转换为大写。 |
valueOf() |
返回指定字符串对象的原始值。 |
var strTxt=new String("MyTypeScript");
var concatStr=" hello world!";
//concat 连接两个或更多字符串,并返回新的字符串
let fullTxt=strTxt.concat(concatStr);
console.log(`concat后的字符串:${fullTxt}`);//输出结果 MyTypeScript hello world!
//charAt 返回在指定位置的字符
console.log(`charAt范围0处的字符串:${strTxt.charAt(0)}`);// 输出结果 M
console.log(`charAt范围12处的字符串:${strTxt.charAt(12)}`);// 输出结果
//charCodeAt 返回在指定的位置的字符的 Unicode 编码
console.log(`charCodeAt 0处的字符串:${strTxt.charCodeAt(0)}`);// 输出结果 77
console.log(`charCodeAt 12处的字符串:${strTxt.charCodeAt(12)}`);// 输出结果 NAN
//indexOf 返回某个指定的字符串值在字符串中首次出现的位置
console.log(`indexOf:${strTxt.indexOf("y")}`);//输出结果 1
console.log(`indexOf:${strTxt.indexOf("G")}`);//输出结果 -1
//lastIndexOf 从后向前搜索字符串,并从起始位置(0)开始计算返回字符串最后出现的位置
console.log(`lastIndexOf:${strTxt.lastIndexOf("y")}`);//输出结果 3
console.log(`lastIndexOf:${strTxt.lastIndexOf("G")}`);//输出结果 -1
//localeCompare 用本地特定的顺序来比较两个字符串
console.log(`localeCompare:${strTxt.localeCompare("MyTypeScript")}`);//输出结果 0
console.log(`localeCompare:${strTxt.localeCompare(concatStr)}`);//输出结果 1
//match 查找找到一个或多个正则表达式的匹配
console.log(`match:${strTxt.match(/p/g)}`);//输出结果 p,p
console.log(`match:${strTxt.match(/G/g)}`);//输出结果 null
//search 检索与正则表达式相匹配的值
console.log(`match:${strTxt.search(/p/g)}`);//输出结果 4
console.log(`match:${strTxt.search(/G/g)}`);//输出结果 -1
//replace 替换与正则表达式匹配的子串
console.log(`replace:${concatStr.replace("!","")}`);//输出结果 hello world
//split 把字符串分割为子字符串数组
var txtSplit="my,your,her,his";
console.log(txtSplit.split(","));//输出结果 [ 'my', 'your', 'her', 'his' ]
console.log(txtSplit.split(",",2));//输出结果 [ 'my', 'your' ]
//substring 提取字符串中两个指定的索引号之间的字符
console.log(`substring:${strTxt.substring(2)}`);//输出结果 TypeScript
console.log(`substring:${strTxt.substring(2,4)}`);//输出结果 Ty
//toUpperCase 字符转大写
console.log(`toUpperCase:${strTxt.toUpperCase()}`);//输出结果 MYTYPESCRIPT
//toLowerCase 字符转小写
console.log(`toLowerCase:${strTxt.toLowerCase()}`);//输出结果 mytypescript
定义数组方式:
// 在元素类型后面加上[]
let arr1: number[] = [1, 2];
//使用数组泛型
let arr: Array = [1, 2];
console.log(arr);
//使用Arrary对象
//空数组
let arrNums:number[]=new Array();
console.log(arrNums);
arrNums.push(50);//数组新增元素
console.log(arrNums);
//指定长度的数组
let arrNums1:number[]=new Array(4);//长度为4的空数组
console.log(arrNums1);//输出结果 [ <4 empty items> ]
arrNums1.push(20);//数组新增元素
arrNums1.push(30);//数组新增元素
arrNums1.push(40);//数组新增元素
arrNums1.push(50);//数组新增元素
arrNums1.push(60);//数组新增元素
console.log(arrNums1);//输出结果 [ <4 empty items>, 20, 30, 40, 50, 60 ]
//直接初始化数组元素
var sites:string[] = new Array("Google","RunTS","Taobao","Facebook")
console.log(sites);//输出结果 [ 'Google', 'RunTS', 'Taobao', 'Facebook' ]
//数组元素赋值给变量
var arr:number[] = [12,13];
var[x,y] = arr; // 将数组的两个元素赋值给变量 x 和 y 等价于 var x=arr[0];var y=arr[1];
console.log(x);// 输出结果 12
console.log(y);//输出结果 13
一个数组的元素可以是另外一个数组,这样就构成了多维数组,最简单的多维数组就是二维数组,定时方式:var arr_name:datatype[][]=[ [val1,val2,val3],[v1,v2,v3] ]
var multi:number[][] = [[1,2,3],[23,24,25]];
console.log(multi[0][0]);
console.log(multi[0][1]);
console.log(multi[0][2]);
console.log(multi[1][0]);
console.log(multi[1][1]);
console.log(multi[1][2]);
常用数组方法
方法 |
描述 |
concat() |
连接两个或更多的数组,并返回结果 |
every() |
检测数值元素的每个元素是否都符合条件 |
filter() |
检测数值元素,并返回符合条件所有元素的数组 |
forEach() |
数组每个元素都执行一次回调函数 |
indexOf() |
搜索数组中的元素,并返回它所在的位置,如果搜索不到,返回值 -1,代表没有此项 |
join() |
把数组的所有元素放入一个字符串。 |
lastIndexOf() |
返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索 |
map() |
通过指定函数处理数组的每个元素,并返回处理后的数组 |
pop() |
删除数组的最后一个元素并返回删除的元素 |
push() |
向数组的末尾添加一个或更多元素,并返回新的长度 |
reduce() |
将数组元素计算为一个值(从左到右) |
reduceRight() |
将数组元素计算为一个值(从右到左) |
reverse() |
反转数组的元素顺序 |
shift() |
删除数组的第一个元素并返回删除的第一个元素 |
slice() |
选取数组的的一部分,并返回一个新数组 |
some() |
检测数组元素中是否有元素符合指定条件 |
sort() |
对数组的元素进行排序 |
splice() |
从数组中添加或删除元素 |
toString() |
把数组转换为字符串,并返回结果 |
unshift() |
向数组的开头添加一个或更多元素,并返回新的长度 |
var alpha = ["a", "b", "c"];
// concat方法,只能同种类型的数组连接
var concatArr = ["1", "2", "3"];
let fullConcat = alpha.concat(concatArr);
console.log("fullConcat : " + fullConcat );//输出结果 a,b,c,1,2,3
//push 向数组的末尾添加一个或更多元素,并返回新的长度
let pushArr=alpha.push("d");
console.log(`push后的新数组 : ${alpha} 新的数组长度:${pushArr}`);//输出结果 push后的新数组 : a,b,c,d 新的数组长度:4
//pop 删除数组的最后一个元素并返回删除的元素
let popArr=alpha.pop();
console.log(`pop后的新数组 : ${alpha} 删除的元素:${popArr}` );// 输出结果 pop后的新数组 : a,b,c 删除的元素:d
//shift 删除数组的第一个元素并返回删除的第一个元素
let shiftArr=concatArr.shift();
console.log(`shift后的新数组:${concatArr};删除的第一个元素${shiftArr}`);//输出结果 shift后的新数组:2,3;删除的第一个元素1
//unshift 向数组的开头添加一个或更多元素,并返回新的长度
let unshiftArr=concatArr.unshift("1");
console.log(`unshift后的新数组:${concatArr};新的数组长度${unshiftArr}`);//输出结果 unshift后的新数组:1,2,3;新的数组长度3
//splice 从数组中添加或删除元素
let addItem=concatArr.splice(2,0,"4"); //参数 第一个表示开始位置的索引编码,第二个表示删除的字符数,第三个参数表示新增的元素
console.log(`splice添加元素后的数组:${concatArr};新增的元素${addItem}`); //输出结果 splice添加元素后的数组:1,2,4,3;新增的元素
let delItem=concatArr.splice(2,1);
console.log(`splice删除元素后的数组:${concatArr};删除的元素${delItem}`); //输出结果 splice删除元素后的数组:1,2,3;删除的元素4
//indexOf 搜索数组中的元素,并返回它所在的位置,如果搜索不到,返回值 -1,代表没有此项。
let cindex=alpha.indexOf('c');
console.log(`查找字母c的索引编号:${cindex}`);//输出结果 2
let dindex=alpha.indexOf('d');
console.log(`查找字母d的索引编号:${dindex}`);//输出结果 -1
//lastIndexOf 返回一个指定的字符串值最后出现的位置,在一个字符串中的指定位置从后向前搜索。
let cLastIndex=alpha.lastIndexOf("c");
console.log(`查找字母c的最后出现的索引编号:${cLastIndex}`);//输出结果 2
//join 把数组的所有元素放入一个字符串
let arrToStr=alpha.join();
console.log(`join后的结果:${arrToStr}`);//输出结果 a,b,c
//toString 把数组转换为字符串,并返回结果
let strArr=alpha.toString();
console.log(`toString后的结果:${strArr}`);//输出结果 a,b,c
//forEach() 数组每个元素都执行一次回调函数
alpha.forEach(item=>{ console.log(`forEach遍历 当前值:${item}`);});
//every方法 检测数值元素的每个元素是否都符合条件
var everyArrary=new Array(1,2,3,4,5,6);
let eResult=everyArrary.every((val,indexVal,arrary)=>{
return val%2==0;
});
console.log(`every遍历检测每个元素除以2余数都为0结果:${eResult}`);//输出结果 false
//some 检测数组元素中是否有元素符合指定条件
let someResult=everyArrary.some((val,indexVal,arrary)=>{ return val%8==0;});
console.log(`some遍历检测存在8的倍数结果:${someResult}`);//输出结果 false
let someResult1=everyArrary.some((val,indexVal,arrary)=>{ return val%2==0;});
console.log(`some 遍历检测存在2的倍数结果:${someResult1}`);//输出结果 true
//filter方法,检测数值元素,并返回符合条件所有元素的数组
var filterArrary=new Array(10,20,30,40,50,60);
let rArrary=filterArrary.filter((val,indexVal,arrary)=>{return (val>=20&&val<=40)});
console.log(`filter过滤20-40之间的整数${rArrary}`);//输出结果 20,30,40
//slice 选取数组的的一部分,并返回一个新数组
var sliceArr=filterArrary.slice(1,3);// 第一个参数表示开始索引编码 第二个参数表示结束索引编码
console.log(`slice选取部分数据:${sliceArr}`);//输出结果 20,30
//sort 对数组的元素进行排序
var sortNumArr=[1,3,4,6,5,8,10,9];
var sortTxtArr=["a","d","f","c"];
console.log(`sortNum数组排序前的数据:${sortNumArr}`);//输出结果 1,3,4,6,5,8,10,9
sortNumArr.sort();
console.log(`sortNum数组排序后的数据:${sortNumArr}`);//输出结果 1,10,3,4,5,6,8,9
console.log(`sortTxt数组排序前的数据:${sortTxtArr}`);//输出结果 a,d,f,c
sortTxtArr.sort();
console.log(`sortTxt数组排序后的数据:${sortTxtArr}`);//输出结果 a,c,d,f
//reverse 反转数组的元素顺序
var reverseNumArr=[1,3,4,6,5,8,10,9];
var reverseTxtArr=["a","d","f","c"];
console.log(`reverseNum数组排序前的数据:${reverseNumArr}`);//输出结果 1,3,4,6,5,8,10,9
reverseNumArr.reverse();
console.log(`reverseNum数组排序后的数据:${reverseNumArr}`);//输出结果 9,10,8,5,6,4,3,1
reverseNumArr.reverse();
console.log(`reverseNum数组二次排序后的数据:${reverseNumArr}`);//输出结果 1,3,4,6,5,8,10,9
console.log(`reverseTxt数组排序前的数据:${reverseTxtArr}`);//输出结果 a,d,f,c
reverseTxtArr.reverse();
console.log(`reverseTxt数组排序后的数据:${reverseTxtArr}`);//输出结果 c,f,d,a
//reduce 将数组元素计算为一个值(从左到右)
var reduceArrary=[20,40,7,8];
let reduceNum=reduceArrary.reduce(function(preVal,curVal,curIndex,arrary)
{
console.log(`reduce:preVal ${preVal},curVal ${curVal},curIndex ${curIndex}, arrary ${arrary}`);
return preVal+curVal;
});
console.log(`reduce计算结果:${reduceNum}`);//输出结果 75
//reduceRight 将数组元素计算为一个值(从右到左)
let reduceRightNum=reduceArrary.reduceRight(function(preVal,curVal,curIndex,arrary)
{
console.log(`reduceRight:preVal ${preVal},curVal ${curVal},curIndex ${curIndex}, arrary ${arrary}`);
return preVal+curVal;
});
console.log(`reduceRight计算结果:${reduceNum}`);//输出结果 75
//map 通过指定函数处理数组的每个元素,并返回处理后的数组
var mapNumArrary=[1,2,3,4];
let newNumArr=new Array();
//void函数
let mapArr1=mapNumArrary.map(function(val,index,arrary){
newNumArr.push(val);
});
console.log(newNumArr);//输出结果 [ 1, 2, 3, 4 ]
console.log(mapArr1);//输出结果 [ undefined, undefined, undefined, undefined ]
//有返回值的函数
let mapArr2=mapNumArrary.map(function(val,index,arrary){
return val*2;
});
console.log(mapArr2);//输出结果 [ 2, 4, 6, 8 ]
元组类型用来表示已知元素数量和类型的数组,各元素的类型不必相同,对应位置的类型需要相同
let x: [string, number];
x = ['TypeScript', 1]; // 运行正常
x = [1, 'TypeScript']; // 报错 Type 'number' is not assignable to type 'string'. Type 'string' is not assignable to type 'number'.
console.log(x[0]); // 输出 TypeScript
var mytuple = [10,"Hello","World","typeScript"];
mytuple.push(12);// 添加到元组中
console.log(`添加后元组数据:${mytuple}`);//10,Hello,World,typeScript,12
mytuple.pop();//删除元组最后一个元素
console.log(`删除后元组数据:${mytuple}`);//10,Hello,World,typeScript
var mytuple = [10, "RunTs", "Taobao", "Google"]; // 创建一个元组
console.log("元组的第一个元素为:" + mytuple[0]);
// 更新元组元素
mytuple[0] = 121;
console.log("元组中的第一个元素更新为:"+ mytuple[0]);
var a =[10,"RunTs"];
var [b,c] = a;
console.log(b);//输出结果 10
console.log(c);//输出结果 RunTs
enum类型是对JavaScript标准数据类型的一个补充,默认情况下,从0开始为元素编号。 你也可以手动的指定成员的数值,或者全部都采用手动赋值。枚举类型提供的一个便利是你可以由枚举的值得到它的名字
//默认0元素开始编号
enum Color {Red, Green, Blue};
let c: Color = Color.Green;
let colorName: string = Color[2];//获取枚举值名称
console.log(colorName);//输出结果 Blue
//从1开始编号
enum Color1 {Red = 1, Green, Blue};
let c1: Color1 = Color1.Green;
let colorName1: string = Color1[2];
console.log(colorName1);//输出结果 Green
//全部手动赋值
enum Color2 {Red = 2, Green = 3, Blue = 4};
let c2: Color2 = Color2.Green;
let colorName2: string = Color2[2];
console.log(colorName2);//输出结果 Red
let colorName3: string =Color2[Color2.Red];
console.log(colorName3);//输出结果 Red
null
在 JavaScript 中 null 表示 "什么都没有"。null是一个只有一个值的特殊类型,表示一个空对象引用。用 typeof 检测 null 返回是 object。
undefined
在 JavaScript 中, undefined 是一个没有设置值的变量。
typeof 一个没有值的变量会返回 undefined。
Null 和 Undefined 是其他任何类型(包括 void)的子类型,可以赋值给其它类型,如数字类型,此时,赋值后的类型会变成 null 或 undefined。而在TypeScript中启用严格的空校验(--strictNullChecks)特性,就可以使得null 和 undefined 只能被赋值给 void 或本身对应的类型
// 启用 --strictNullChecks
let x: number;
x = 1; // 正确
x = undefined; // 错误 Type 'undefined' is not assignable to type 'number'.
x = null; // 错误 Type 'null' is not assignable to type 'number'.
// 启用 --strictNullChecks
let y: number | null | undefined;
y= 1; // 正确
y = undefined; // 正确
y = null; // 正确
never 是其它类型(包括 null 和 undefined)的子类型,代表从不会出现的值。这意味着声明为 never 类型的变量只能被 never 类型所赋值,在函数中它通常表现为抛出异常或无法执行到终止点(例如无限循环)
let x: never;
let y: number;
// 错误,Type 'number' is not assignable to type 'never'.
x = 123;
// 运行正确,never 类型可以赋值给 never类型
x = (()=>{ throw new Error('exception')})();
// 运行正确,never 类型可以赋值给 数字类型
y = (()=>{ throw new Error('exception')})();
// 返回值为 never 的函数可以是抛出异常的情况
function error(message: string): never {
throw new Error(message);
}
// 返回值为 never 的函数可以是无法被执行到的终止点的情况
function loop(): never {
while (true) {}
}
有时候你会遇到这样的情况,你会比TypeScript更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
类型断言有两种形式。 其一是“尖括号”语法:
let someValue: any = "this is a string";
let strLength: number = (someValue).length;
console.log(strLength);//输出结果16
另一个为as语法:
let someValue: any = "this is a string";
let strLength: number = (someValue as string ).length;
console.log(strLength);//输出结果16
联合类型(Union Types)可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。
注意:只能赋值指定的类型,如果赋值其它类型就会报错。
创建联合类型的语法格式:Type1|Type2|Type3
var val:string|number;
val = 12;
console.log("数字为 "+ val);
val = "RunTs";
console.log("字符串为 " + val);
//赋值其他类型的数据就会报错
val=true;//编译报错 Type 'boolean' is not assignable to type 'string | number'.
//联合类型作为函数参数使用
function f(personName:string|string[]){
console.log(`人员姓名 ${personName}`);
}
f("TypeScript");
f(["TS","JS"]);
//数组声明为联合类型
var arr:string[]|number[];
arr=["TS","JS"];
console.log(arr);
arr=[1,2,3,4,5];
console.log(arr);
变量是一种使用方便的占位符,用于引用计算机内存地址。我们可以把变量看做存储数据的容器。
TypeScript 变量的命名规则:
1)声明变量的类型及初始值
var [变量名] : [类型] = 值;
2)声明变量的类型,但没有初始值,变量值会设置为 undefined
var [变量名] : [类型]
3)声明变量并初始值,但不设置类型,该变量可以是任意类型
var [变量名] = 值;
4)声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为 undefined
var [变量名];
//声明变量的类型及初始值
var myName:string="TypeScript";
console.log(myName);
//声明变量的类型,但没有初始值,变量值会设置为 undefined
var myAge:number;
console.log(myAge);//编译报错 Variable 'myAge' is used before being assigned.
//声明变量并初始值,但不设置类型,该变量可以是任意类型
var myHight=165;
console.log(typeof(myHight));//输出结果 number
//声明变量没有设置类型和初始值,类型可以是任意类型,默认初始值为 undefined
var myWeight;
console.log(myWeight);// 输出结果 undefined
var的作用域:
var global_num = 12; //全局变量
//Nmbers 类
class Numbers {
num_val = 13; // 实例变量
static sval = 10;// 静态变量
storeNum():void {
var local_num = 14; // 局部变量
console.log(local_num);
}
}
console.log("全局变量为: "+global_num);// 输出结果 全局变量为: 12
console.log(Numbers.sval);// Numbers 静态变量 输出结果 10
var obj = new Numbers();
console.log("Numbers实例变量: "+obj.num_val);//输出结果 Numbers实例变量: 13
obj.storeNum();//输出结果 14
变量获取特别之处
for (var i = 0; i < 10; i++) {
setTimeout(function() { console.log(i); }, 100 * i);
}
此代码的返回结果
我们期待的返回结果应该是
原因分析: setTimeout在若干毫秒后执行一个函数,并且是在for循环结束后。 for循环结束后,i的值为10。 所以当函数被调用的时候,它会打印出 10!
一个通常的解决方法是使用立即执行的函数表达式(IIFE)来捕获每次迭代时i的值
for (var i = 0; i < 10; i++) {
// capture the current state of 'i'
// by invoking a function with its current value
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}
let用来声明变量,有以下几个特点
let与var声明变量比较
特点 | let | var | 备注 |
作用域 | 代码块内 | 全局范围内 | for循环计数器很适合用let |
重复声明 | 不可以 | 可以 |
//代码块内有效
{
let a = 0;
var b = 1;
}
console.log(a); // 编译报错 Cannot find name 'a'.
console.log(b); // 1
//不允许重复声明
let c = 1; //编译报错 Cannot redeclare block-scoped variable 'c'.
let c = 2;//编译报错 Cannot redeclare block-scoped variable 'c'.
var d = 3;
var d = 4;
console.log(c);
console.log(d); //输出结果 4
//拥有块级作用域的变量的另一个特点是,它们不能在被声明之前读或写。 虽然这些变量始终“存在”于它们的作用域里,但在直到声明它的代码之前的区域都属于 时间死区。 它只是用来说明我们不能在 let语句之前访问它们
console.log(e); //编译报错 Variable 'e' is used before being assigned.
let e = "apple";
const关键字用来声明常量,const声明有以下特点:
{
var myName="TypeScript";
const myAge=30;
//不可修改值
myAge=40; //编译报错 Cannot assign to 'myAge' because it is a constant.
//声明必须赋初始值
const cityName; //编译报错 'const' declarations must be initialized.
}
console.log(myName);
console.log(myAge);// 编译报错 Cannot find name 'myAge'.
运算符 |
描述 |
+ |
加法 |
- |
减法 |
* |
乘法 |
/ |
除法 |
% |
取模(余数) |
++ |
自增 |
-- |
自减 |
运算符 |
描述 |
== |
等于 |
!= |
不等于 |
> |
大于 |
< |
小于 |
>= |
大于或等于 |
<= |
小于或等于 |
运算符 |
描述 |
&& |
and |
|| |
or |
! |
not |
运算符 |
描述 |
例子 |
类似于 |
结果 |
十进制 |
& |
AND,按位与处理两个长度相同的二进制数,两个相应的二进位都为 1,该位的结果值才为 1,否则为 0。 |
x = 5 & 1 |
0101 & 0001 |
0001 |
1 |
| |
OR,按位或处理两个长度相同的二进制数,两个相应的二进位中只要有一个为 1,该位的结果值为 1。 |
x = 5 | 1 |
0101 | 0001 |
0101 |
5 |
~ |
取反,取反是一元运算符,对一个二进制数的每一位执行逻辑反操作。使数字 1 成为 0,0 成为 1。 |
x = ~ 5 |
~0101 |
1010 |
-6 |
^ |
异或,按位异或运算,对等长二进制模式按位或二进制数的每一位执行逻辑异按位或操作。操作的结果是如果某位不同则该位为 1,否则该位为 0。 |
x = 5 ^ 1 |
0101 ^ 0001 |
0100 |
4 |
<< |
左移,把 << 左边的运算数的各二进位全部左移若干位,由 << 右边的数指定移动的位数,高位丢弃,低位补 0。 |
x = 5 << 1 |
0101 << 1 |
1010 |
10 |
>> |
右移,把 >> 左边的运算数的各二进位全部右移若干位,>> 右边的数指定移动的位数。 |
x = 5 >> 1 |
0101 >> 1 |
0010 |
2 |
>>> |
无符号右移,与有符号右移位类似,除了左边一律使用0 补位。 |
x = 2 >>> 1 |
0010 >>> 1 |
0001 |
1 |
运算符 |
描述 |
= |
赋值 |
+= |
先进行加运算后赋值 |
-= |
先进行减运算后赋值 |
*= |
先进行乘运算后赋值 |
/= |
先进行除运算后赋值 |
三元运算有 3 个操作数,并且需要判断布尔表达式的值。该运算符的主要是决定哪个值应该赋值给变量。
语法:Test ? expr1 : expr2
let myName:string="TypeScript";
let isTS=myName=="TypeScript"?true:false; //等价于 if(myName==""){isTS=true;} else { isTS=true;}
let myName:string="TypeScript";
console.log(typeof(myName));//输出结果 string
let myage=30;
console.log(typeof(myage));//输出结果 number
条件语句分为:
if....else if....else语句注意事项
switch语句需要遵循的规则
switch(expression){
case constant-expression :
statement(s);
break; /* 可选的 */
case constant-expression :
statement(s);
break; /* 可选的 */
/* 您可以有任意数量的 case 语句 */
default : /* 可选的 */
statement(s);
}
语法
for ( init; condition; increment ){
statement(s);
}
示例
let count:number=10;
for(let i=0;i
for...in 语句用于一组值的集合或列表进行迭代输出
语法
for (var val in list) {
//语句
}
示例
let arraryInput=[1,2,3,4,5];
for(let j in arraryInput){
console.log(j);
}
for...of 语句创建一个循环来迭代可迭代的对象。在 ES6 中引入的 for...of 循环,以替代 for...in 和 forEach() ,并支持新的迭代协议。for...of 允许你遍历 Arrays(数组), Strings(字符串), Maps(映射), Sets(集合)等可迭代的数据结构等。
因为 forEach 在 iteration 中是无法返回的,所以可以使用 every 和 some 来取代 forEach。
let someArray = [1, "string", false];
//for...of循环
for (let entry of someArray) {
console.log(entry); // 1, "string", false
}
//forEach循环
someArray.forEach((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引 index${indexVal}`);
console.log(`当前array ${array}`);
});
//every循环 有返回值
//默认只遍历循环第一个元素,返回值为false
let returnEvery=someArray.every((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引index ${indexVal}`);
console.log(`当前array ${array}`);
});
console.log(returnEvery);//false
//会循环遍历所有元素,返回值为true
returnEvery=someArray.every((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引index ${indexVal}`);
console.log(`当前array ${array}`);
return true;//continue
});
console.log(returnEvery);//true
//只会循环遍历第一个元素,返回值为false
returnEvery=someArray.every((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引index ${indexVal}`);
console.log(`当前array ${array}`);
return false;//quit
});
console.log(returnEvery);//false
//会循环遍历所有元素,返回值为true
returnEvery=someArray.every((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引index ${indexVal}`);
console.log(`当前array ${array}`);
return "ok";//quit
});
console.log(returnEvery);//true
//some循环
//默认遍历所有元素 返回值 false
let someReturn=someArray.some((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引index ${indexVal}`);
console.log(`当前array ${array}`);
});
console.log(someReturn);//false
//只会遍历第一个元素 返回值 true
someReturn=someArray.some((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引index ${indexVal}`);
console.log(`当前array ${array}`);
return true;
});
console.log(someReturn);//true
//会遍历所有元素 返回值false
someReturn=someArray.some((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引index ${indexVal}`);
console.log(`当前array ${array}`);
return false;
});
console.log(someReturn);//false
//只会遍历第一个元素 返回值true
someReturn=someArray.some((val, indexVal, array) => {
console.log(`当前值 ${val}`);
console.log(`当前索引index ${indexVal}`);
console.log(`当前array ${array}`);
return 'ok';
});
console.log(someReturn);//true
语法
while(condition)
{
statement(s);
}
示例
var num:number = 5;
var factorial:number = 1;
while(num >=1) {
factorial = factorial * num;
num--;
}
console.log("5 的阶乘为:"+factorial);
语法
do
{
statement(s);
}while( condition );
条件表达式出现在循环的尾部,所以循环中的 statement(s) 会在条件被测试之前至少执行一次
示例
var n:number = 10;
do {
console.log(n);
n--;
} while(n>=0);
break 语句有以下两种用法:
如果您使用的是嵌套循环(即一个循环内嵌套另一个循环),break 语句会停止执行最内层的循环,然后开始执行该块之后的下一行代码。
var i:number = 1;
while(i<=10) {
if (i % 5 == 0) {
console.log ("在 1~10 之间第一个被 5 整除的数为 : "+i);
break; // 找到一个后退出循环
}
i++;
} // 输出 5 然后程序执行结束
//循环内嵌套循环
let testArrary=[1,2,3,4,5];
let testArrary2=[3,4,5,6,10];
for(let i of testArrary)
{
for(let j of testArrary2)
{
if((i*2)==j)
{
console.log(`i:${i} j:${j}`);
break;
}
}
}
continue 语句有点像 break 语句。但它不是强制终止,continue 会跳过当前循环中的代码,强迫开始下一次循环。
对于 for 循环,continue 语句执行后自增语句仍然会执行。对于 while 和 do...while 循环,continue 语句重新执行条件判断语句。
var num:number = 0;
var count:number = 0;
for(num=0;num<=20;num++) {
if (num % 2==0) {
continue;
}
count++;
}
console.log ("0 ~20 之间的奇数个数为: "+count) //输出10个偶数
无限循环就是一直在运行不会停止的循环。 for 和 while 循环都可以创建无限循环。
for 创建无限循环语法格式:
for(;;) {
// 语句
}
示例
for(;;) {
console.log("这段代码会不停的执行")
}
while 创建无限循环语法格式:
while(true) {
// 语句
}
示例
while(true) {
console.log("这段代码会不停的执行")
}
对象是包含一组键值对的实例。 值可以是标量、函数、数组、对象等
var houseData=
{
door:"white door",
windows:"",
bedNum:5,
TVBrand:"XiaoMi",
bedRoomNum:5,
cars:["Red Car","Green Car","Black Car"],
keyOfRoomNum:function(){},//函数模型
houseDesc:function(){ return "MyHouse";}
};
console.log(houseData.door);//输出结果 white door
houseData.keyOfRoomNum=function(){
console.log("房间钥匙总共15把");
};
houseData.keyOfRoomNum();// 输出结果 房间钥匙总共15把
console.log(houseData.houseDesc());// 输出结果 MyHouse
console.log(houseData.cars);//输出结果 [ 'Red Car', 'Green Car', 'Black Car' ]
鸭子类型(Duck Typing)
鸭子类型(英语:duck typing)是动态类型的一种风格,是多态(polymorphism)的一种形式。
在这种风格中,一个对象有效的语义,不是由继承自特定的类或实现特定的接口,而是由"当前方法和属性的集合"决定。
在鸭子类型中,关注点在于对象的行为能做什么,而不是关注对象所属的类型。
interface IPoint{
xPoint:number,
yPoint:number
}
function addPoints(p1:IPoint,p2:IPoint):IPoint {
let xPointSum = p1.xPoint + p2.xPoint;
let yPointSum = p1.yPoint + p2.yPoint;
return {xPoint:xPointSum,yPoint:yPointSum};
}
var newPoint = addPoints({xPoint:3,yPoint:4},{xPoint:5,yPoint:1});
console.log(newPoint);//输出结果 { xPoint: 8, yPoint: 5 }
//错误 Argument of type '{ xPoint: number; }' is not assignable to parameter of type 'IPoint'. Property 'yPoint' is missing in type '{ xPoint: number; }' but required in type 'IPoint'.
var newPoint2 = addPoints({xPoint:1},{xPoint:4,yPoint:3});
Map 对象保存键值对,并且能够记住键的原始插入顺序,任何值(对象或者原始值) 都可以作为一个键或一个值
Map函数与属性
let namesMap=new Map();
//设置map
namesMap.set("1","TypeScript");
namesMap.set("2","JavaScript");
namesMap.set("3","ES6");
//数量
console.log(namesMap.size);// 输出结果 3
//循环遍历 Map中的key
for (let key of namesMap.keys()) {
console.log(key);
}
//循环遍历 Map中的value
for (let value of namesMap.values()) {
console.log(value);
}
//循环遍历 Map中的key => value
for (let entry of namesMap.entries()) {
console.log(entry[0], entry[1]);
}
//循环遍历——使用对象解析
for (let [key, value] of namesMap) {
console.log(key, value);
}
//获取键值对应的value值
console.log(namesMap.get("1"));//输出结果 TypeScript
//判断是否包含key
console.log(namesMap.has("1"));// 输出结果 true
console.log(namesMap.has("7"));// 输出结果 false
//删除键值对
console.log(namesMap.delete("3"));// 输出结果 true
console.log(namesMap);// 输出结果 Map(2) { '1' => 'TypeScript', '2' => 'JavaScript' }
console.log(namesMap.delete("7"));// 输出结果 false
//清除
namesMap.clear();
console.log(namesMap.size);// 输出结果 0
函数是一组一起执行一个任务的语句,可以把代码划分到不同的函数中,如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。
function MyTs(){
console.log("欢迎来到TypeScript的世界");
}
MyTs();
语法
function function_name():return_type {
// 语句
return value;
}
示例
function MyTWithVal():string{
return "欢迎来到TypeScript的世界";
}
console.log(MyTWithVal());
基础带参函数
function fWithPara(studentName:string,age:number){
console.log(`姓名为 ${studentName}的学生,年龄是${age}岁`);
}
fWithPara("王明",18);
可选带参函数
可选参数使用问号标识 ?
function fSelPara(companyName:string,simpleName?:string){
if(simpleName){
console.log(`公司名称为 ${companyName}的简称是${simpleName}`);
}
else {
console.log(`公司名称为 ${companyName}`);
}
}
fSelPara("阿里巴巴","淘宝");//输出结果 公司名称为 阿里巴巴的简称是淘宝
fSelPara("阿里巴巴");//输出结果 公司名称为 阿里巴巴
默认带参函数
语法
function function_name(param1[:type],param2[:type] = default_value) {
}
示例
function fDefault(studentName:string,age:number=0){
console.log(`姓名为 ${studentName}的学生,年龄是${age}岁`);
}
fDefault("王明",18);//输出结果 姓名为 王明的学生,年龄是18岁
fDefault("王明");//输出结果 姓名为 王明的学生,年龄是0岁
注意:参数不能同时设置为可选和默认
剩余参数函数
我们不知道要向函数传入多少个参数,这时候我们就可以使用剩余参数来定义
function fRePara(mainGroupName:string,...otherGroupName:string[]){
if(otherGroupName.length>0){
console.log(`主队为 ${mainGroupName},其他队${otherGroupName}`);
}
else{
console.log(`主队为 ${mainGroupName}`);
}
}
fRePara("第一梯队","第二梯队","第三梯队");//输出结果 主队为 第一梯队,其他队第二梯队,第三梯队
fRePara("第一梯队");//输出结果 主队为 第一梯队
匿名函数是一个没有函数名的函数,匿名函数在程序运行时动态声明,除了没有函数名外,其他的与标准函数一样。我们可以将匿名函数赋值给一个变量,这种表达式就成为函数表达式。
语法
var res = function( [arguments] ) { ... }
示例
//匿名函数
var showTime=function(){
var curDate=new Date();
console.log(`当前日期:${curDate.getFullYear()}年${curDate.getMonth()+1}月${curDate.getDate()}日`);
}
showTime();
//匿名函数自调用
(function(){
console.log("hello world");
}())
TypeScript 也支持使用 JavaScript 内置的构造函数 Function() 来定义函数
语法
var res = new Function ([arg1[, arg2[, ...argN]],] functionBody)
参数说明
示例
var myFunction = new Function("a", "b", "return a * b");
var x = myFunction(4, 3);
console.log(x);//输出结果 12
语法
( [param1, param2,…param n] )=>statement;
示例
//带参箭头函数
var func=(x:number,y:number)=>{
let sum=x+y;
console.log(sum);
}
func(20,30);
//单个带参箭头函数
//标准写法
var funcSS=(x:number)=>{
console.log(x);
}
funcSS(10);
//可以省略()
var funcS=x=>{
console.log(x);
}
funcS(50);
//无参数箭头函数
var testLambda=()=>{
console.log("这是没有参数的箭头函数");
}
testLambda();
重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
示例
function disp(s1:string):void;
function disp(n1:number,s1:string):void;
function disp(x:any,y?:any):void {
console.log(x);
console.log(y);
}
disp("abc");//输出结果 abc undefined
disp(1,"xyz");//输出结果 1 xyz
接口是一系列抽象方法的声明,是一些方法特征的集合,这些方法都应该是抽象的,需要由具体的类去实现,然后第三方就可以通过这组抽象方法调用,让具体的类执行具体的方法,定义接口的关键字为interface
//标准接口
interface IPerson{
PersonName:string,
Height:number,
weight:number,
Gender:string
}
//定义一个类型为IPerson的变量,变量中的字段必须和接口中的一致(字段名称和字段数量及字段类型),否则会编译报错
let commonPerson:IPerson=
{
PersonName:"接口类型的变量",
Height:167,
weight:116,
Gender:"女",
}
console.log(commonPerson.PersonName);
commonPerson.PersonName="接口类型的变量测试";
console.log(commonPerson.PersonName);
//定义一个类,继承IPerson,类中必须包含IPerson中所有的字段,否则编译报错;类中的字段可以比接口中的字段多
class PersonInfo implements IPerson
{
PersonName: string;
Height: number;
weight: number;
Gender: string;
TelNum:string;
DetailAddress:string;
}
let personTest=new PersonInfo();
personTest.PersonName="类继承接口";
personTest.TelNum="13775175856";
console.log(personTest.PersonName);
//带有函数的接口定义
interface IFunc
{
/**新增用户
*userName:用户姓名
*/
AddUser(userName:string):void,
GetUserName(userId:number):string,
DeleteUser(userId:number):boolean,
GetUserList():void
}
//定义一个类,继承IFunc接口
class MyFunc implements IFunc
{
AddUser(userName:string):void
{
console.log("新增用户:"+userName);
}
GetUserName(userId: number): string
{
console.log("获取用户姓名:"+userId);
return "获取用户名称";
}
DeleteUser(userId: number): boolean
{
console.log("删除用户:"+userId);
return true;
}
GetUserList(): void
{
console.log("获取用户列表");
}
/**获取用户分页列表
* pageIndex:页码
* pageSize:每页数量
*/
GetUserPage(pageIndex:number,pageSize:number):void {
}
}
let myfunc=new MyFunc();
myfunc.AddUser("张三");
let delResult=myfunc.DeleteUser(1);
console.log(delResult);
和类一样,接口也可以相互扩展。 这让我们能够从一个接口里复制成员到另一个接口里,可以更灵活地将接口分割到可重用的模块里
一个接口可以继承多个接口,创建出多个接口的合成接口
interface IPerson{
PersonName:string,
Height:number,
weight:number,
Gender:string
}
//单个接口继承
interface IPersonDetail extends IPerson
{
Address:string,
TelNum:string,
Email:string
}
//多个接口继承
interface IUser extends IPerson,IPersonDetail
{
UserName:string
}
当接口继承了一个类类型时,它会继承类的成员但不包括其实现。 就好像接口声明了所有类中存在的成员,但并没有提供具体实现一样。 接口同样会继承到类的private和protected成员。 这意味着当你创建了一个接口继承了一个拥有私有或受保护的成员的类时,这个接口类型只能被这个类或其子类所实现(implement)。
这是很有用的,当你有一个很深层次的继承,但是只想你的代码只是针对拥有特定属性的子类起作用的时候。子类除了继承自基类外与基类没有任何联系。
//定义类
class PersonDetail
{
Address:string;
TelNum:string;
Email:string;
private personCompany:string="";
protected companyName:string="";
}
//接口继承类
interface IPerson extends PersonDetail
{
PersonName:string,
Height:number,
weight:number,
Gender:string
}
//定义类,继承 IPerson
// 对于 PersonDetail类中的private及protected成员 编译报错 Type 'PersonInfo' is missing the following properties from type 'IPerson': personCompany, companyName
class PersonInfo implements IPerson
{
PersonName:string;
Height:number;
weight:number;
Gender:string;
Address:string;
TelNum:string;
Email:string;
}
//类继承类 再继承接口 ,必须包含接口中的字段,否则编译报错
class Control extends PersonDetail implements IPerson
{
private state: any;
PersonName:string;
Height:number;
weight:number;
Gender:string
}
interface ICounter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): ICounter {
let counter = function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
类描述了所创建的对象共同的属性和方法
定义类的关键字为 class,后面紧跟类名,类可以包含以下几个模块(类的数据成员):
//只有字段的类
class ClassInfo{
classNo:string;//班级名称字段
studentNum:number;//学生数字段
}
var classInfo=new ClassInfo();
classInfo.classNo="高三一班";
classInfo.studentNum=50;
console.log(`${classInfo.classNo}共有学生${classInfo.studentNum}名`);
// 字段、构造函数、方法同事包含的类
class Car {
// 字段
engine:string;
// 构造函数
constructor(engine:string) {
this.engine = engine;
}
// 方法
disp():void {
console.log("函数中显示发动机型号:"+this.engine)
}
}
// 创建一个对象
var obj = new Car("XXSY1");
// 访问字段
console.log("读取发动机型号 : "+obj.engine);
// 访问方法
obj.disp();
TypeScript 支持继承类,即我们可以在创建类的时候继承一个已存在的类,这个已存在的类称为父类,继承它的类称为子类。类继承使用关键字 extends,子类除了不能继承父类的私有成员(方法和属性)和构造函数,其他的都可以继承。TypeScript 一次只能继承一个类,不支持继承多个类,但 TypeScript 支持多重继承(A 继承 B,B 继承 C)。
//类的继承
class AreaInfo{
Area:number;//面积
Unit:string;//面积单位
}
class Circle extends AreaInfo{
Diam:number;//直径
}
var mycircle=new Circle();
mycircle.Diam=10;
mycircle.Area=500;
mycircle.Unit="平方米";
console.log(mycircle);
//重写继承类的方法
class PrinterClass {
doPrint():void {
console.log("父类的 doPrint() 方法。")
}
}
class StringPrinter extends PrinterClass {
doPrint():void {
super.doPrint(); // 调用父类的函数
console.log("子类的 doPrint()方法。")
}
}
注意:子类只能继承一个父类,TypeScript 不支持继承多个类,但支持多重继承
static关键字
static 关键字用于定义类的数据成员(属性和方法)为静态的,静态成员可以直接通过类名调用
class ClassInfo{
static classNo:string;
static showInfo(){
console.log("我是班级"+this.classNo+"中的一员");
}
}
ClassInfo.classNo="高三一班";
ClassInfo.showInfo();
instanceof运算符
instanceof 运算符用于判断对象是否是指定的类型,如果是返回 true,否则返回 false
class Person{ }
var obj = new Person()
var isPerson = obj instanceof Person;
console.log("obj 对象是 Person 类实例化来的吗? " + isPerson);
TypeScript 中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。TypeScript 支持 3 种不同的访问权限:
class Person{
public PersonName:string;//人员姓名
protected LinkTel:string;//联系电话
private MoneyNum:number;
}
var perInfo=new Person();
perInfo.PersonName="马云";
perInfo.LinkTel="";//编译报错 Property 'LinkTel' is protected and only accessible within class 'Person' and its subclasses.
perInfo.MoneyNum=50000000000;//编译报错 Property 'MoneyNum' is private and only accessible within class 'Person'.
readonly关键字将属性设置为只读的,只读属性必须在声明时或构造函数里被初始化
class Octopus {
readonly name: string;
readonly numberOfLegs: number = 8;
constructor (theName: string) {
this.name = theName;
}
}
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // Cannot assign to 'name' because it is a read-only propert
TypeScript支持通过getters/setters来截取对对象成员的访问。 它能帮助你有效的控制对对象成员的访问
let passcode = "secret passcode";
class Employee {
private _fullName: string;
get fullName(): string {
return this._fullName;
}
set fullName(newName: string) {
if (passcode && passcode == "secret passcode") {
this._fullName = newName;
}
else {
console.log("Error: Unauthorized update of employee!");
}
}
}
let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
console.log(employee.fullName);
}
对于存取器有下面几点需要注意的:
首先,存取器要求你将编译器设置为输出ECMAScript 5或更高。 不支持降级到ECMAScript 3。 其次,只带有 get不带有set的存取器自动被推断为readonly。 这在从代码生成 .d.ts文件时是有帮助的,因为利用这个属性的用户会看到不允许够改变它的值。
抽象类做为其它派生类的基类使用。 它们一般不会直接被实例化。 不同于接口,抽象类可以包含成员的实现细节。abstract关键字是用于定义抽象类和在抽象类内部定义抽象方法。
抽象类中的抽象方法不包含具体实现并且必须在派生类中实现。 抽象方法的语法与接口方法相似。 两者都是定义方法签名但不包含方法体。 然而,抽象方法必须包含 abstract
关键字并且可以包含访问修饰符。
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log('Department name: ' + this.name);
}
abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
constructor() {
super('Accounting and Auditing'); // constructors in derived classes must call super()
}
printMeeting(): void {
console.log('The Accounting Department meets each Monday at 10am.');
}
generateReports(): void {
console.log('Generating accounting reports...');
}
}
let department: Department; // ok to create a reference to an abstract type
department = new Department(); // error: cannot create an instance of an abstract class
department = new AccountingDepartment(); // ok to create and assign a non-abstract subclass
department.printName();
department.printMeeting();
department.generateReports(); // error: Property 'generateReports' does not exist on type 'Department'.
类可以实现接口,使用关键字 implements,并将 interest 字段作为类的属性使用
interface ILoan {
interest:number;
}
class AgriLoan implements ILoan {
interest:number;
rebate:number;
constructor(interest:number,rebate:number) {
this.interest = interest;
this.rebate = rebate;
}
}
var obj = new AgriLoan(10,1);
console.log("利润为 : "+obj.interest+",抽成为 : "+obj.rebate);
TypeScript 模块的设计理念是可以更换的组织代码。
模块是在其自身的作用域里执行,并不是在全局作用域,这意味着定义在模块里面的变量、函数和类等在模块外部是不可见的,除非明确地使用 export 导出它们。类似地,我们必须通过 import 导入其他模块导出的变量、函数、类等。
两个模块之间的关系是通过在文件级别上使用 import 和 export 建立的。
模块使用模块加载器去导入其它的模块。 在运行时,模块加载器的作用是在执行此模块代码前去查找并执行这个模块的所有依赖。 大家最熟知的JavaScript模块加载器是服务于 Node.js 的 CommonJS 和服务于 Web 应用的 Require.js,此外还有有 SystemJs 和 Webpack。
任何声明(比如变量,函数,类,类型别名或接口)都能够通过添加export
关键字来导出
export interface StringValidator
{
IsAcceptable(s: string): boolean;
}
导出语句
导出语句很便利,因为我们可能需要对导出的部分重命名
class UserRepository
{
GetUserName(userId:number):string
{
return "查询用户:"+userId;
}
}
export {UserRepository};//导出语句
export{ UserRepository as UserRep};//导出语句并重命名
重新导出
我们经常会去扩展其它模块,并且只导出那个模块的部分内容。 重新导出功能并不会在当前模块导入那个模块或定义一个新的局部变量
Validation.ts
export interface StringValidator
{
IsAcceptable(s: string): boolean;
}
ZipCodeValidator.ts
import { StringValidator } from './Validation';
const numberRegexp = /^[0-9]+$/;//数字验证正则表达式
/**邮政编码验证 */
export class ZipCodeValidator implements StringValidator {
IsAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
myTS.ts
export class ParseIntBasedZipCodeValidator
{
isAcceptable(s: string)
{
return s.length === 5 && parseInt(s).toString() === s;
}
}
// 导出原先的验证器但做了重命名
export {ZipCodeValidator as RegExpBasedZipCodeValidator} from "./ZipCodeValidator";
或者一个模块可以包裹多个模块,并把他们导出的内容联合在一起通过语法:export * from "module"
AllValidators.ts
export * from "./StringValidator"; // exports interface StringValidator
export * from "./ZipCodeValidator"; // exports class ZipCodeValidator
默认导出
每个模块都可以有一个default
导出。 默认导出使用 default
关键字标记;并且一个模块只能够有一个default
导出, 需要使用一种特殊的导入形式来导入 default
导出。
default
导出十分便利。 比如,像JQuery这样的类库可能有一个默认导出 jQuery
或$
,并且我们基本上也会使用同样的名字jQuery
或$
导出JQuery。
JQuery.d.ts
declare let $: JQuery;
export default $;
App.ts
import $ from "JQuery";
$("button.continue").html( "Next Step..." );
类和函数声明可以直接被标记为默认导出, 标记为默认导出的类和函数的名字是可以省略的。
模块的导入操作与导出一样简单。 可以使用以下 import
形式之一来导入其它模块中的导出内容
导入一个模块中的某个导出内容
import { ZipCodeValidator } from "./ZipCodeValidator";
let myValidator = new ZipCodeValidator();
对导入内容重命名
import { ZipCodeValidator as ZCV} from "./ZipCodeValidator";
let letValid=new ZCV();
将整个模块导入到一个变量,并通过它来访问模块的导出部分
import * as validator from "./ZipCodeValidator";
let myZipCodeValid = new validator.ZipCodeValidator();
具有副作用的导入模块
尽管不推荐这么做,一些模块会设置一些全局状态供其它模块使用。 这些模块可能没有任何的导出或用户根本就不关注它的导出。 使用下面的方法来导入这类模块:
import "./my-module.js";
export =
和 import = require()
CommonJS和AMD都有一个exports
对象的概念,它包含了一个模块的所有导出内容。
它们也支持把exports
替换为一个自定义对象。 默认导出就好比这样一个功能;然而,它们却并不相互兼容。 TypeScript模块支持 export =
语法以支持传统的CommonJS和AMD的工作流模型。
export =
语法定义一个模块的导出对象。 它可以是类,接口,命名空间,函数或枚举。
若要导入一个使用了export =
的模块时,必须使用TypeScript提供的特定语法import let = require("module")
。
ZipCodeValidator.ts
let numberRegexp = /^[0-9]+$/;
class ZipCodeValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
export = ZipCodeValidator;
Test.ts
import zip = require("./ZipCodeValidator");
// Some samples to try
let strings = ["Hello", "98052", "101"];
// Validators to use
let validator = new zip();
// Show whether each string passed each validator
strings.forEach(s => {
console.log(`"${ s }" - ${ validator.isAcceptable(s) ? "matches" : "does not match" }`);
});
命名空间一个最明确的目的就是解决重名问题。命名空间定义了标识符的可见范围,一个标识符可在多个名字空间中定义,它在不同名字空间中的含义是互不相干的。这样,在一个新的名字空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其他名字空间中。TypeScript 中命名空间使用namespace关键字定义,语法如下:
namespace SomeNameSpaceName {
export interface ISomeInterfaceName { }
export class SomeClassName { }
}
以上定义了一个命名空间 SomeNameSpaceName,如果我们需要在外部可以调用 SomeNameSpaceName 中的类和接口,则需要在类和接口添加 export 关键字。
要在另外一个命名空间调用语法格式为:
SomeNameSpaceName.SomeClassName;
如果一个命名空间在一个单独的 TypeScript 文件中,则应使用三斜杠 /// 引用它,语法格式如下:
///
命名空间不在单独的ts文件中的使用
/**验证器 */
namespace Validation
{
const lettersRegexp = /^[A-Za-z]+$/;//字母验证表达式
const numberRegexp = /^[0-9]+$/;//数字验证正则表达式
export interface StringValidator
{
IsAcceptable(s: string): boolean;
}
/**字母验证 */
export class LettersOnlyValidator implements StringValidator
{
IsAcceptable(s: string)
{
return lettersRegexp.test(s);
}
}
/**邮政编码验证 */
export class ZipCodeValidator implements StringValidator
{
IsAcceptable(s: string)
{
return s.length === 5 && numberRegexp.test(s);
}
}
}
let strings = ["Hello", "98052", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
for (let name in validators)
{
console.log(`"${ s }" - ${ validators[name].IsAcceptable(s) ? "matches" : "does not match" } ${ name }`);
}
});
命名空间在单独文件的使用
我们把上面的Validation拆分成四个ts文件
Validation.ts
namespace Validation
{
export interface StringValidator
{
IsAcceptable(s: string): boolean;
}
}
LettersValidator.ts
///
namespace Validation
{
const lettersRegexp = /^[A-Za-z]+$/;//字母验证表达式
/**字母验证 */
export class LettersOnlyValidator implements StringValidator
{
IsAcceptable(s: string)
{
return lettersRegexp.test(s);
}
}
}
ZipCodeValidator.ts
///
namespace Validation
{
const numberRegexp = /^[0-9]+$/;//数字验证正则表达式
/**邮政编码验证 */
export class ZipCodeValidator implements StringValidator
{
IsAcceptable(s: string)
{
return s.length === 5 && numberRegexp.test(s);
}
}
}
myTS.ts
///
///
///
let strings = ["Hello", "98052", "101"];
// Validators to use
let validators: { [s: string]: Validation.StringValidator; } = {};
validators["ZIP code"] = new Validation.ZipCodeValidator();
validators["Letters only"] = new Validation.LettersOnlyValidator();
// Show whether each string passed each validator
strings.forEach(s => {
for (let name in validators)
{
console.log(`"${ s }" - ${ validators[name].IsAcceptable(s) ? "matches" : "does not match" } ${ name }`);
}
});
当涉及到多文件时,我们必须确保所有编译后的代码都被加载了,我们有两种方式。
第一种方式,把所有的输入文件编译为一个输出文件,编译器会根据源码里的引用标签自动地对输出进行排序,需要使用--outFile
标记:
tsc --outFile sample.js myTS.ts
第二种方式,编译每一个文件(默认方式),那么每个源文件都会对应生成一个JavaScript文件,页面上通过
目前,大多数流行的全局访问型库实际上都以UMD库的形式进行书写(见后文)。 UMD库的文档很难与全局库文档两者之间难以区分。 在书写全局声明文件前,一定要确认一下库是否真的不是UMD。
从代码上识别全局库
全局库的代码通常都十分简单。 一个全局的“Hello, world”库可能是这样的:
function createGreeting(s) {
return "Hello, " + s;
}
或这样:
window.createGreeting = function(s) {
return "Hello, " + s;
}
当你查看全局库的源代码时,你通常会看到:
var
语句或function
声明window.someName
document
或window
是存在的你不会看到:
require
或define
var fs = require("fs");
define(...)
调用require
或导入这个库一些库只能工作在模块加载器的环境下。 比如,像 express
只能在Node.js里工作所以必须使用CommonJS的require
函数加载。
ECMAScript 2015(也就是ES2015,ECMAScript 6或ES6),CommonJS和RequireJS具有相似的导入一个模块的表示方法。 例如,对于JavaScript CommonJS (Node.js),有下面的代码
var fs = require("fs");
对于TypeScript或ES6,import
关键字也具有相同的作用:
import fs = require("fs");
你通常会在模块化库的文档里看到如下说明:
var someLib = require('someLib');
或
define(..., ['someLib'], function(someLib) {
});
与全局模块一样,你也可能会在UMD模块的文档里看到这些例子,因此要仔细查看源码和文档。
从代码上识别模块化库
模块库至少会包含下列具有代表性的条目之一:
require
或define
import * as a from 'b';
or export c;
这样的声明exports
或module.exports
它们极少包含:
window
或global
的赋值UMD模块是指那些既可以作为模块使用(通过导入)又可以作为全局(在没有模块加载器的环境里)使用的模块。 许多流行的库,比如 Moment.js,就是这样的形式。 比如,在Node.js或RequireJS里,你可以这样写:
import moment = require("moment");
console.log(moment.format());
然而在纯净的浏览器环境里你也可以这样写:
console.log(moment.format());
识别UMD库
UMD模块会检查是否存在模块加载器环境。 这是非常形容观察到的模块,它们会像下面这样:
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(["libName"], factory);
} else if (typeof module === "object" && module.exports) {
module.exports = factory(require("libName"));
} else {
root.returnExports = factory(root.libName);
}
}(this, function (b) {
如果你在库的源码里看到了typeof define
,typeof window
,或typeof module
这样的测试,尤其是在文件的顶端,那么它几乎就是一个UMD库。
UMD库的文档里经常会包含通过require
“在Node.js里使用”例子, 和“在浏览器里使用”的例子,展示如何使用标签去加载脚本。
一个模块插件可以改变一个模块的结构(UMD或模块)。 例如,在Moment.js里, moment-range
添加了新的range
方法到monent
对象。
对于声明文件的目标,我们会写相同的代码不论被改变的模块是一个纯粹的模块还是UMD模块。
module-plugin.d.ts模板:
// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
// Project: [~THE PROJECT NAME~]
// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
/*~ This is the module plugin template file. You should rename it to index.d.ts
*~ and place it in a folder with the same name as the module.
*~ For example, if you were writing a file for "super-greeter", this
*~ file should be 'super-greeter/index.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;
}
}
一个全局插件是全局代码,它们会改变全局对象的结构。 对于 全局修改的模块,在运行时存在冲突的可能。
比如,一些库往Array.prototype
或String.prototype
里添加新的方法。
识别全局插件
全局通常很容易地从它们的文档识别出来。
你会看到像下面这样的例子:
var x = "hello, world";
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());
当一个全局修改的模块被导入的时候,它们会改变全局作用域里的值。 比如,存在一些库它们添加新的成员到String.prototype
当导入它们的时候。 这种模式很危险,因为可能造成运行时的冲突, 但是我们仍然可以为它们书写声明文件。
识别全局修改的模块
全局修改的模块通常可以很容易地从它们的文档识别出来。 通常来讲,它们与全局插件相似,但是需要 require
调用来激活它们的效果。
你可能会看到像下面这样的文档:
// 'require' call that doesn't use its return value
var unused = require("magic-string-time");
/* or */
require("magic-string-time");
var x = "hello, world";
// Creates new methods on built-in types
console.log(x.startsWithHello());
var y = [1, 2, 3];
// Creates new methods on built-in types
console.log(y.reverseAndSort());
可能会有以下几种依赖
1.依赖全局库
如果你的库依赖于某个全局库,使用///
指令:
///
function getThing(): someLib.thing;
2.依赖模块
如果你的库依赖于模块,使用import
语句:
import * as moment from "moment";
function getThing(): moment;
3.依赖UMD库
1)从全局库
如果你的全局库依赖于某个UMD模块,使用///
///
function getThing(): moment;
2)从一个模块或UMD库
如果你的模块或UMD库依赖于一个UMD库,使用import
语句:
import * as someLib from 'someLib';
不要使用///
防止命名冲突
注意,在书写全局声明文件时,允许在全局作用域里定义很多类型。 我们十分不建义这样做,当一个工程里有许多声明文件时,它会导致无法处理的命名冲突。
一个简单的规则是使用库定义的全局变量名来声明命名空间类型。 比如,库定义了一个全局的值 cats
,你可以这样写
declare namespace cats {
interface KittySettings { }
}
不要
// at top-level
interface CatsKittySettings { }
这样也保证了库在转换成UMD的时候没有任何的破坏式改变,对于声明文件用户来说。
ES6模块插件的影响
一些插件添加或修改已存在的顶层模块的导出部分。 当然这在CommonJS和其它加载器里是允许的,ES模块被当作是不可改变的因此这种模式就不可行了。 因为TypeScript是能不预知加载器类型的,所以没在编译时保证,但是开发者如果要转到ES6模块加载器上应该注意这一点。
ES6模块调用签名的影响
很多流行库,比如Express,暴露出自己作为可以调用的函数。 比如,典型的Express使用方法如下:
import exp = require("express");
var app = exp();
在ES6模块加载器里,顶层的对象(这里以exp
导入)只能具有属性; 顶层的模块对象 永远不能被调用。 十分常见的解决方法是定义一个 default
导出到一个可调用的/可构造的对象; 一会模块加载器助手工具能够自己探测到这种情况并且使用 default
导出来替换顶层对象。
声明 |
描述 |
示例 |
declare var |
声明变量 |
declare var foo:number; |
declare const |
声明只读变量 |
declare const foo:number; |
declare let |
声明拥有块级作用域使用的变量 |
declare let foo:number; |
declare function |
声明函数 |
declare function greet(greeting: string): void; |
declare namespace |
描述用点表示法访问的类型或值 |
// 示例 全局变量myLib包含一个makeGreeting函数, 还有一个属性 numberOfGreetings指示目前为止欢迎数量 declare namespace myLib { function makeGreeting(s: string): string; let numberOfGreetings: number; } |
declare calss |
描述一个类或像类一样的对象。 类可以有属性和方法,就和构造函数一样 |
declare class Greeter { constructor(greeting: string); greeting: string; showGreeting(): void; } |