TypeScript(三)类型断⾔、数组的类型、函数的类型
TypeScript(三)类型断⾔、数组的类型、函数的类型
⽂章⽬录
⽹址:ts.xcatliu/
1. 数组的类型
在 TypeScript 中,数组类型有多种定义⽅式,⽐较灵活。
「类型 + ⽅括号」表⽰法
最简单的⽅法是使⽤「类型 + ⽅括号」来表⽰数组:
let fibonacci:number[]=[1,1,2,3,5];
数组的项中不允许出现其他的类型:
let fibonacci:number[]=[1,'1',2,3,5];
/
/ Type 'string' is not assignable to type 'number'.
数组的⼀些⽅法的参数也会根据数组在定义时约定的类型进⾏限制:
let fibonacci:number[]=[1,1,2,3,5];
fibonacci.push('8');
// Argument of type '"8"' is not assignable to parameter of type 'number'.
上例中,push ⽅法只允许传⼊ number 类型的参数,但是却传了⼀个 "8" 类型的参数,所以报错了。这⾥ "8" 是⼀个字符串字⾯量类型,会在后续章节中详细介绍。
数组泛型
我们也可以使⽤数组泛型(Array Generic) Array<elemType> 来表⽰数组:
let fibonacci:Array<number>=[1,1,2,3,5];
关于泛型,可以参考⼀章。
⽤接⼝表⽰数组
接⼝也可以⽤来描述数组:
interface NumberArray {
[index:number]:number;
}
let fibonacci: NumberArray =[1,1,2,3,5];
NumberArray 表⽰:只要索引的类型是数字时,那么值的类型必须是数字。
虽然接⼝也可以⽤来描述数组,但是我们⼀般不会这么做,因为这种⽅式⽐前两种⽅式复杂多了。
不过有⼀种情况例外,那就是它常⽤来表⽰类数组。
类数组
类数组(Array-like Object)不是数组类型,⽐如 arguments:
function sum(){
let args:number[]= arguments;
}
// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
上例中,arguments 实际上是⼀个类数组,不能⽤普通的数组的⽅式来描述,⽽应该⽤接⼝:
function sum(){
let args:{
[index:number]:number;
length:number;
callee:Function;
}= arguments;
}
在这个例⼦中,我们除了约束当索引的类型是数字时,值的类型必须是数字之外,也约束了它还有 length 和 callee 两个属性。
事实上常⽤的类数组都有⾃⼰的接⼝定义,如 IArguments, NodeList, HTMLCollection 等:
function sum(){
let args: IArguments = arguments;
}
其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
interface IArguments {
[index:number]:any;
length:number;
callee:Function;
}
关于内置对象,可以参考⼀章。
any 在数组中的应⽤
⼀个⽐较常见的做法是,⽤ any 表⽰数组中允许出现任意类型:
let list:any[]=['xcatliu',25,{ website:'xcatliu'}];
参考
([中⽂版](zhongsp.gitbooks.io/typescript-handbook/content/doc/handbook/Basic Types.html#数组))()
2. 函数的类型
函数声明
在 JavaScript 中,有两种常见的定义函数的⽅式——函数声明(Function Declaration)和函数表达式(Function Expression):
// 函数声明(Function Declaration)
function sum(x, y){
return x + y;
}
// 函数表达式(Function Expression)
let mySum=function(x, y){
return x + y;
};
⼀个函数有输⼊和输出,要在 TypeScript 中对其进⾏约束,需要把输⼊和输出都考虑到,其中函数声明的类型定义较简单:
function sum(x:number, y:number):number{
return x + y;
}
typeof array
注意,输⼊多余的(或者少于要求的)参数,是不被允许的:
function sum(x:number, y:number):number{
return x + y;
}
sum(1,2,3);
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.
function sum(x:number, y:number):number{
return x + y;
}
sum(1);
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.
函数表达式
如果要我们现在写⼀个对函数表达式(Function Expression)的定义,可能会写成这样:
let mySum=function(x:number, y:number):number{
return x + y;
};
这是可以通过编译的,不过事实上,上⾯的代码只对等号右侧的匿名函数进⾏了类型定义,⽽等号左边的 mySum,是通过赋值操作进⾏类型推论⽽推断出来的。如果需要我们⼿动给 mySum 添加类型,则应该是这样:
let mySum:(x:number, y:number)=>number=function(x:number, y:number):number{
return x + y;
};
注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>。
在 TypeScript 的类型定义中,=> ⽤来表⽰函数的定义,左边是输⼊类型,需要⽤括号括起来,右边是输出类型。
==在 ES6 中,=> 叫做箭头函数,应⽤⼗分⼴泛,可以参考 。
⽤接⼝定义函数的形状
我们也可以使⽤接⼝的⽅式来定义⼀个函数需要符合的形状:
interface SearchFunc {
(source:string, subString:string):boolean;
}
let mySearch: SearchFunc;
mySearch=function(source:string, subString:string){
return source.search(subString)!==-1;
}
采⽤函数表达式|接⼝定义函数的⽅式时,对等号左侧进⾏类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。
可选参数
前⾯提到,输⼊多余的(或者少于要求的)参数,是不允许的。那么如何定义可选的参数呢?
与接⼝中的可选属性类似,我们⽤ ? 表⽰可选的参数:
function buildName(firstName:string, lastName?:string){
if(lastName){
return firstName +' '+ lastName;
}else{
return firstName;
}
}
let tomcat =buildName('Tom','Cat');
let tom =buildName('Tom');
需要注意的是,可选参数必须接在必需参数后⾯。换句话说,可选参数后⾯不允许再出现必需参数了:
function buildName(firstName?:string, lastName:string){
if(firstName){
return firstName +' '+ lastName;
}else{
return lastName;
}
}
let tomcat =buildName('Tom','Cat');
let tom =buildName(undefined,'Tom');
// index.ts(1,40): error TS1016: A required parameter cannot follow an optional parameter.
参数默认值
在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数:
function buildName(firstName:string, lastName:string='Cat'){
return firstName +' '+ lastName;
}
let tomcat =buildName('Tom','Cat');
let tom =buildName('Tom');
此时就不受「可选参数必须接在必需参数后⾯」的限制了:
function buildName(firstName:string='Tom', lastName:string){
return firstName +' '+ lastName;
}
let tomcat =buildName('Tom','Cat');
let cat =buildName(undefined,'Cat');
关于默认参数,可以参考 。
剩余参数
ES6 中,可以使⽤ ...rest 的⽅式获取函数中的剩余参数(rest 参数):
function push(array,...items){
items.forEach(function(item){
array.push(item);
});
}
let a: any[]=[];
push(a,1,2,3);
事实上,items 是⼀个数组。所以我们可以⽤数组的类型来定义它:
function push(array:any[],...items:any[]){
items.forEach(function(item){
array.push(item);
});
}
let a =[];
push(a,1,2,3);
注意,rest 参数只能是最后⼀个参数,关于 rest 参数,可以参考 。
重载
重载允许⼀个函数接受不同数量或类型的参数时,作出不同的处理。
⽐如,我们需要实现⼀个函数 reverse,输⼊数字 123 的时候,输出反转的数字 321,输⼊字符串 'hello' 的时候,输出反转的字符串
'olleh'。
利⽤联合类型,我们可以这么实现:
function reverse(x:number|string):number|string|void{
if(typeof x ==='number'){
return String().split('').reverse().join(''));
}else if(typeof x ==='string'){
return x.split('').reverse().join('');
}
}
然⽽这样有⼀个缺点,就是不能够精确的表达,输⼊为数字的时候,输出也应该为数字,输⼊为字符串的时候,输出也应该为字符串。
这时,我们可以使⽤重载定义多个 reverse 的函数类型:
function reverse(x:number):number;
function reverse(x:string):string;
function reverse(x:number|string):number|string|void{
if(typeof x ==='number'){
return String().split('').reverse().join(''));
}else if(typeof x ==='string'){
return x.split('').reverse().join('');
}
}
上例中,我们重复定义了多次函数 reverse,前⼏次都是函数定义,最后⼀次是函数实现。在编辑器的代码提⽰中,可以正确的看到前两个提⽰。
注意,TypeScript 会优先从最前⾯的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前⾯。
参考

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。