typescript函数类型-typescript – 函数类型

1 function student(x:string,y:number):string{
2     return `我是${x},今年${y}岁`;
3 }
4 
5 console.log(student("wzy",22));  // 我是wzy,今年22岁

形式与JavaScript中的函数声明相同,有​​什么区别?

(1)指定参数的类型(因为有类型检测,所以必须遵循),然后指定返回值的类型,

这时返回值类型可以省略,因为Typescript会根据返回语句手动推导出返回值类型。

(2)参数不能多也不能少,恰到好处,且与顺序有关。

1 function student(x:string,y:number){
2     console.log(`我是${x},今年${y}岁`);
3 }
4 
5 student(22,"wzy"); 
6 //  Argument of type '22' is not assignable to parameter of type 'string'.

2. 函数表达式

有些词总是需要复制的,比如下面两句话

如果我们今天要写一个函数表达式(Function Expression)的定义,可能会这样写:

1 let student = function(x:string,y:number):string{
2     return `我是${x},今年${y}岁`;
3 }
4 
5 console.log(student("wzy",22));  // 我是wzy,今年22岁

这样就可以编译通过了,但实际上,上面的代码只是定义了等号右边匿名函数的类型,而等号右边的同学是通过形参运算进行类型推导来确定的。

如果我们需要自动给students添加类型,应该是这样的:

1 let student:(x:string,y:number)=>string = function(x:string,y:number):string{
2     return `我是${x},今年${y}岁`;
3 }
4 
5 console.log(student("wzy",22)); // 我是wzy,今年22岁

这还需要多说几句:

(1)前后参数名称可以不一致

 1 let student:(x:string,y:number)=>string = function(name:string,age:number):string{
 2     return `我是${x},今年${y}岁`;
 3 }
 4 // 这样对吗?嗯!好像是哪里不太对~
 5 // Cannot find name 'x'.   
 6 // Cannot find name 'y'.
 7 
 8 let student:(x:string,y:number)=>string = function(name:string,age:number):string{
 9     return `我是${name},今年${age}岁`;
10 }
11 // 这样就对了~

(2)我对这些声明函数的形式的理解是:

let student : (x:string,y:number)=>string = function(name:string,age:number):string{}
  声明变量             指定函数类型                     根据指定函数类型定义函数
此时再根据之前讲过的“类型推论”,就有以下这样的声明方式了

1 let student:(x:string,y:number)=>string = function(name,age){
2     return `我是${name},今年${age}岁`;
3 }
4 5 let student = function(name:string,age:number):string{
6     return `我是${name},今年${age}岁`;
7 }

 

据说这叫“按上下文分类”typescript函数类型,是类型推断的一种。

(3)我们之前也说过,没有返回值的时候怎么办,在这些完整的函数声明的情况下,返回值不能为空,这时候我们都会想到void

 1 let student:(x:string,y:number) = function(name,age){  
 2     console.log(`我是${name},今年${age}岁`);
 3 }
 4 
 5 //  '=>' expected.
 6 // 这样是不允许的,也不要妄想   =>""  这样,人家就没有""这种类型好吗
 7 
 8 let student:(x:string,y:number)=>void = function(name,age){  
 9     console.log(`我是${name},今年${age}岁`);
10 }
11 // 还是老老实实的这样子吧

3.socket中函数的定义

1 interface Student{
2     (x:string,y:number):string
3 }
4 
5 let str1:Student;
6 str1 = function(x,y){
7     return `我是${x},今年${y}岁`; 
8 }

4、可选参数

前面说过,函数的参数是必不可少的,但是也可以像socket一样使用吗?表​​示可选参数

 1 function student(name:string,age:number,sex?:boolean){
 2     if(sex){
 3         return `name:${name},age:${age},sex:女`;
 4     }else{
 5         return `name:${name},age:${age},sex:未知`;
 6     }
 7 }
 8   
 9 console.log(student("weizeyang",22,true));   // name:weizeyang,age:22,sex:女
10 console.log(student("weizeyang",22));        // name:weizeyang,age:22,sex:未知

我们来看看它需要注意什么:

(1) 可选参数应放在必需参数之前

(2) 胃痛、不开心

5、默认参数

在打字稿中,您可以为函数中的参数设置默认值。 当用户不传递该参数或者传递的值未定义时,将显示默认值。

 1 function student(name:string,age:number,sex="女"){
 2     return `name:${name},age:${age},sex:${sex}`;
 3 }
 4   
 5 console.log(student("weizeyang",22,"未知"));  
 6 // name:weizeyang,age:22,sex:未知
 7 
 8 console.log(student("weizeyang",22));   
 9 // name:weizeyang,age:22,sex:女
10 
11 console.log(student("weizeyang",22,undefined));   
12 // name:weizeyang,age:22,sex:女

各个年龄段的注意事项:

(1) 可以看出,默认值的参数被识别为可选参数,但不需要位于必需参数的前面。

(2)虽然是可选参数,但并不需要放在必需参数前面,但是一旦放在必需参数上面,就必须明确写上undefined或者一个值,即不能是空的。

这样,写在上面就不是可选参数了。

 1 function student(sex="女",name:string,age:number){
 2     return `name:${name},age:${age},sex:${sex}`;
 3 }
 4   
 5 console.log(student("未知","weizeyang",22));  
 6 // name:weizeyang,age:22,sex:未知
 7 
 8 console.log(student("weizeyang",22));   
 9 // Expected 3 arguments, but got 2.
10 
11 console.log(student(undefined,"weizeyang",22));   
12 // name:weizeyang,age:22,sex:女

我不知道其他人是否能理解这句话,但无论如何我明白它在说什么。

6. 其余参数

上面提到的必选参数、默认参数、可选参数都代表了某个参数。 有时候,你想同时操作多个参数,或者你不知道要传入多少个参数。

在 JavaScript 中,您可以使用参数来访问所有传入参数。

在打字稿中,您可以将所有参数收集到一个变量中:

1 function student(age:number,sex:boolean,...name:string[]){
2     return `我是${name.join("")},年龄${age}`;
3 } 
4 
5 console.log(student(22,true,"wei","ze","yang"));
6 // 我是weizeyang,年龄22

来来来,注意一下:

(1) 其余参数将被视为无限数量的可选参数,可以是无,也可以是任意数量。 还要在所需参数之前。

(2)它是一个链表类型,名称是省略号之前的数组名,这个链表可以在函数体中使用。

(3)调用函数时,不要传入链表,只要一一传入即可。

7. Lambda 表达式及其使用

在 JavaScript 中,只有在调用函数时才能指定 this 的值。 学过JavaScript的人都知道,这是一个令人头疼的问题!

 1 var student = {
 2     names: ["wzy", "wyy", "wxy", "yz"],
 3     getName: function() {
 4         
 5         return function(){
 6             var item = Math.floor(Math.random() * 4);
 7             console.log(this);  
 8 
 9             return {
10                 name: this.names[item]    //  Cannot read property 'names' of undefined
11             }
12         }
13     }
14 }
15 
16 let sname = student.getName();
17 console.log("my name is " + sname().name);

复制的第7行是window,因为这个没有动态绑定

 1 "use strict";   // 严格模式
 2 
 3 var student = {
 4     names: ["wzy", "wyy", "wxy", "yz"],
 5     getName: function() {
 6         
 7         return function(){
 8             var item = Math.floor(Math.random() * 4);
 9             console.log(this);    // undefined
10 
11             return {
12                 name: this.names[item]    //  Cannot read property 'names' of undefined
13             }
14         }
15     }
16 }
17 
18 let sname = student.getName();
19 console.log("my name is " + sname().name);

第9行被复制出来为undefined,看起来和上一行是一样的,但实际上现在是JavaScript严格模式(我们一直都是),打印出来的是undefined。

为了解决这个问题,我们可以在函数返回时绑定正确的this。 这样,无论你以后如何使用它,它都会引用绑定的“student”对象。

我们将函数表达式更改为使用 lambda 表达式 ( () => {} )。 这将在创建函数时指定“this”值,而不是在调用函数时指定。

 1 var student = {
 2     names: ["wzy", "wyy", "wxy", "yz"],
 3     getName: function() {
 4         console.log(this);
 5         // 注意:下面这行是一个lambda,允许我们提前捕捉它
 6         return () => {
 7             var item = Math.floor(Math.random() * 4);
 8             console.log(this);
 9             
10             return {
11                 name: this.names[item]
12             }
13         }
14     }
15 }
16 
17 let sname = student.getName();
18 console.log("my name is " + sname().name);

将第4、8行复制出来

也就是学生的对象,你就完成了

8. 超载

举个栗子:

 1 function schools(type:number|string):number|string{
 2     if(typeof type === "number"){
 3         return type.toFixed(2);
 4     }else if(typeof type === "string"){
 5         return type.substring(0,1);
 6     }
 7 }
 8 
 9 console.log(schools(1));       // 1.00
10 console.log(schools("wzy"));   // w

上述函数的参数和返回值的类型都使用了联合类型,所以我们无法准确表达我传入的是数字类型,而返回值也必须是数字类型。

这时候就引入了重载的概念。

学习Java时,重载是这样的:函数名相同,函数的参数列表不同(包括参数个数和参数类型),返回值类型可以相同也可以不同。

网上的例子如下:

 1 function schools(type:number):number;
 2 function schools(type:string):string;
 3 function schools(type:number|string):number|string{
 4     if(typeof type === "number"){
 5         return type.toFixed(2);
 6     }else if(typeof type === "string"){
 7         return type.substring(0,1);
 8     }
 9 }
10 
11 console.log(schools(1));       // 1.00
12 console.log(schools("wzy"));   // w

也就是说这两个函数之前已经被声明过好几次了

必须要注意的是:

(1)TypeScript会优先从第一个函数定义开始匹配,所以如果多个函数定义存在包含关系typescript函数类型,需要先写出精确的定义。

再举个栗子:

 1 function schools(type:number):number;
 2 function schools(type:string):string;
 3 function schools(type:number | string | boolean):number | string | boolean{
 4     if(typeof type === "number"){
 5         return type.toFixed(2);
 6     }else if(typeof type === "string"){
 7         return type.substring(0,1);
 8     }else if(typeof type === "boolean"){
 9         return true;
10     }
11 }
12 
13 console.log(schools(1));       // 1.00
14 console.log(schools("wzy"));   // w
15 console.log(schools(true));    
16 //  Argument of type 'true' is not assignable to parameter of type 'string'.

(2)说明第3行的代码不属于重载部分,这里只有第1、2行的函数重载起作用。 用这两种以外的其他类型的论点来称呼学校是一个错误。

(3) 第1行和第2行定义了两个函数,第3行只是这两个重载函数的实现。

我在知乎上看到的总结是这么说的,因为很多人认为typescript重载的存在意义不大:

TypeScript的重载是给调用者看的,让调用者知道如何调用(同时tsc也会进行静态检测,防止错误调用)。

IDE 的有用代码提示以及返回值的类型约束。