1、TypeScript中的函数
1.1 定义函数
函数是JavaScript应用程序的基础。 它帮助你实现抽象层,模拟类,信息隐藏和模块。 在TypeScript里,虽然已经支持类,命名空间和模块,但函数仍然是主要的定义 行为的地方。
TypeScript为JavaScript函数添加了额外的功能,让我们可以更容易地使用。
对于JavaScript,定义一个函数可以使用function关键字或者函数表达式,例如:
function sum(a, b){
return a + b;
}
此时函数的参数a,b类型是不确定的,如果你传入两个数字,那么会返回两数之和,如果传入的两个参数中包含了字符串或者两个都是字符串,那么就会返回拼接的字符串。
console.log(sum(1, 2));
console.log(sum(1, '2'));
console.log(sum('one', 'two'));
对于上述情况,我们可能会觉得函数功能变得更加强大了,不仅可以实现加法,还可以完成字符串的拼接,但是,这也存在一定的问题,很难从类型上找到预期的行为,即看到sum这个函数,一般只会想到两数相加,并不会想到字符串拼接。
在强类型语言中,例如c, Java中,定义一个函数,必须显式声明函数的返回值类型和每个参数的类型:
int sum(int a, int b){
return a + b;
}
在JavaScript的基础上,TypeScript为函数和参数增加了类型的约束:
function sum(a: number, b: number): number {
return a + b;
}
let sum = function(a: number, b: number): number { return a + b; };
当我们传入的a,b不是数值类型时,TypeScript便会报错。
我们可以给每个参数添加类型之后再为函数本身添加返回值类型。 TypeScript能够根据返回语句自动推断出返回值类型,因此我们通常省略它。因此,可以简化sum函数:
function sum(a: number, b: number) {
return a + b;
}
1.2 可选参数和默认参数
TypeScript里的每个函数参数都是必须的, 也就是说,传递给一个函数的参数个数必须与函数期望的参数个数一致。
function buildName(firstName: string, lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // ah, just right
JavaScript里,每个参数都是可选的,可传可不传。 没传参的时候,它的值就是undefined。 在TypeScript里我们可以在参数名旁使用 ?
实现可选参数的功能。 比如,我们想让last name是可选的:
function buildName(firstName: string, lastName?: string) {
if (lastName)
return firstName + " " + lastName;
else
return firstName;
}
let result1 = buildName("Bob"); // Bob
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // Bob Adams
可选参数必须跟在必须参数后面。 如果上例我们想让first name是可选的,那么就必须调整它们的位置,把first name放在后面。
对于默认参数,也就是我们可以为函数的参数设置一个默认的值,如果给传入了值,那么就使用传入的值,否则使用默认值:
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // works correctly now, returns "Bob Smith"
let result2 = buildName("Bob", undefined); // still works, also returns "Bob Smith"
let result3 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result4 = buildName("Bob", "Adams"); // Bob Adams
与普通可选参数不同的是,带默认值的参数不需要放在必须参数的后面。 如果带默认值的参数出现在必须参数前面,用户必须明确的传入 undefined值来获得默认值。 例如,我们重写最后一个例子,让 firstName是带默认值的参数:
function buildName(firstName = "Will", lastName: string) {
return firstName + " " + lastName;
}
let result1 = buildName("Bob"); // error, too few parameters
let result2 = buildName("Bob", "Adams", "Sr."); // error, too many parameters
let result3 = buildName("Bob", "Adams"); // okay and returns "Bob Adams"
let result4 = buildName(undefined, "Adams"); // okay and returns "Will Adams"
1.3 剩余参数
必要参数,默认参数和可选参数有个共同点:它们表示某一个参数。 有时,你想同时操作多个参数,或者你并不知道会有多少参数传递进来。 在JavaScript里,你可以使用 arguments对象来访问所有传入的参数。
function sum(a, b){
console.log(arguments)
return a + b;
}
console.log(sum(1, 2));
或者使用展开运算符...
:
function sum(a, ...rest){
console.log(rest)
return a;
}
sum(1, 2, 3, 4);
TypeScript也使用了类似展开运算符来实现剩余参数:
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");
2、泛型
在像C#和Java这样的语言中,可以使用泛型
来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。我们可以使用any完成上述功能:
function identity(arg: any): any {
return arg;
}
但是,这也失去了TypeScript的意义,如果我们传入一个数字,我们只知道任何类型的值都有可能被返回。
我们需要一种方法使返回值的类型与传入参数的类型是相同的。 这里,我们使用了 类型变量
,它是一种特殊的变量,只用于表示类型而不是值。
function identity<T>(arg: T): T {
return arg;
}
我们给identity添加了类型变量T。 T帮助我们捕获用户传入的类型(比如:number),之后我们就可以使用这个类型。 之后我们再次使用了 T当做返回值类型。现在我们可以知道参数类型与返回值类型是相同的了。 这允许我们跟踪函数里使用的类型的信息。
我们把这个版本的identity函数叫做泛型,因为它可以适用于多个类型。 不同于使用 any,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。
泛型函数的使用:
let output = identity<string>("myString"); // type of output will be 'string'
可以看到,使用了identity<string>
的方法调用了泛型函数,但是,其实我们可以简化一下,因为编译器会根据传入的参数自动地帮助我们确定T的类型:
let output = identity("myString"); // type of output will be 'string'
泛型也可以使用数组、接口和类中。
泛型数组:
let a: Array<String> = [1,2,3];//不能将类型“number”分配给类型“String”。ts(2322)
let b: Array<Number> = [1, 2, 3];//all right
泛型接口:
interface IdentityInterface<T> {
(arg: T): T;
}
泛型类:
class IdentityClass<T> {//定义一个泛型类
initValue: T;
add: (x: T, y: T) => T;
}
let res = new IdentityClass<number>();
res.initValue= 0;
res.add = function(x, y) { return x + y; };
总结:
如果事先不能知道类型,或者具体的类型在使用的时候才能确定,那就可以使用泛型。