TypeScript教程
不解的地方:
ts函数的重载
ts函数的接口形式,看例子不太懂
typescript有两周类型的数据:原始数据和对象数据
原始数据类型包括:布尔值、数值、字符串、null
、undefined
以及 ES6 中的新类型 Symbol
。
这里就较为特殊的布尔值,空值作简单介绍。
布尔值
布尔值是最基础的数据类型,在 TypeScript 中,使用 boolean 定义布尔值类型:
let isDone: boolean = false;
// 编译成功
注意,使用构造函数 Boolean 创造的对象不是布尔值:
let createdByNewBoolean: boolean = new Boolean(1);
// index.ts(1,5): error TS2322: Type 'Boolean' is not assignable to type 'boolean'
直接调用 Boolean 也可以返回一个 boolean 类型:
let createdByBoolean: boolean = Boolean(1);
空值
JavaScript 没有空值(Void)的概念,
//在 TypeScript 中,可以用 void 表示没有返回值的函数:
function alertName(): void {
alert('My name is Tom');
}
声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null:
let unusable: void = undefined;
任意值
任意值(Any)用来表示允许赋值为任意类型。
什么是任意值类型
如果是一个普通类型,在赋值过程中改变类型是不被允许的:
let myFavoriteNumber: string = 'seven';myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
但如果是 any
类型,则允许被赋值为任意类型。
let myFavoriteNumber: any = 'seven';myFavoriteNumber = 7;
任意值的属性和方法
在任意值上访问任何属性都是允许的:
let anyThing: any = 'hello';console.log(anyThing.myName);console.log(anyThing.myName.firstName);
也允许调用任何方法:
let anyThing: any = 'Tom';anyThing.setName('Jerry');anyThing.setName('Jerry').sayHello();anyThing.myName.setFirstName('Cat');
可以认为,声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
未声明类型的变量
变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:
let something;something = 'seven';something = 7;something.setName('Tom');
等价于
let something: any;something = 'seven';something = 7;something.setName('Tom');
什么是类型推论
以下代码虽然没有指定类型,但是会在编译的时候报错:
let myFavoriteNumber = 'seven';myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
事实上,它等价于:
let myFavoriteNumber: string = 'seven';myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。
联合类型
let myFavoriteNumber: string | number;myFavoriteNumber = 'seven';myFavoriteNumber = 7;
let myFavoriteNumber: string | number;myFavoriteNumber = true;
// index.ts(2,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.// Type 'boolean' is not assignable to type 'number'.
联合类型使用 |
分隔每个类型。
这里的 let myFavoriteNumber: string | number
的含义是,允许 myFavoriteNumber
的类型是 string
或者 number
,但是不能是其他类型。
访问联合类型的属性或方法
访问联合类型的属性,只能访问共有属性;或者等待数据类型确定后,访问对应的属性,看不懂看下面例子
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:
function getLength(something: string | number): number { return something.length;}
// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.// Property 'length' does not exist on type 'number'.
上例中,length
不是 string
和 number
的共有属性,所以会报错。
访问 string
和 number
的共有属性是没问题的:
function getString(something: string | number): string {
return something.toString();
}
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型:
let myFavoriteNumber: string | number;myFavoriteNumber = 'seven';
console.log(myFavoriteNumber.length);
// 5myFavoriteNumber = 7;console.log(myFavoriteNumber.length); // 编译时报错
// index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.
上例中,第二行的 myFavoriteNumber
被推断成了 string
,访问它的 length
属性不会报错。
而第四行的 myFavoriteNumber
被推断成了 number
,访问它的 length
属性时就报错了。
接口对象
简单的例子
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
接口首字母大写
复杂例子
interface Person {
readonly id: number; //readonly修饰id,表示id只读,在第一次给对象Person赋值时候开始,id就不在允许赋值
name: string;
age?: number; // ?表示可选,表示age可以赋值也可以不
[propName: string]: any; // propName: string表示属性名是string类型,注意:接口中的所有属性的类型必须是:后面的子集
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527; // 报错,因为对tom的id进行第二次赋值了
TypeScript中不同方式表示数组
类型+[]
let fibonacci: number[] = [1, 1, 2, 3, 5];
Array<泛型>
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
接口形式表示数组
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1,1,2,3,]
any在数组中的应用
let arr1: any[] = [1,2,3,'2',{'i am jxy'}]
ts函数
声明函数
function sum(x: number, y: number): number {
return x + y;
}
函数的两种表达方式
表达式
let mySum: (x:number, y:number) =>number =function(x:number, y:number):number{ return x+y ; } // =>的左边()中的是输入类型,右边()中的是输出类型
接口形式(不解)
interface SearchFunc{ (source:string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function(source:string, subString:string){ return source.search(subString) !== -1; }
ts函数的参数
可选参数
用 ?
表示可选的参数:
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');
参数默认值
TypeScript 会将添加了默认值的参数识别为可选参数:
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
此时就不受「可选参数必须接在必需参数后面」的限制了
剩余参数
ES6 中,可以使用 ...rest
的方式获取函数中的剩余参数(rest 参数)
在ts中...rest 可以理解为一个数组。所以我们可以用数组的类型来定义它:
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
重载
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
比如,我们需要实现一个函数 reverse
,输入数字 123
的时候,输出反转的数字 321
,输入字符串 'hello'
的时候,输出反转的字符串 'olleh'
。
利用联合类型,我们可以这么实现:
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().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 {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
上例中,我们重复定义了多次函数 reverse
,前几次都是函数定义,最后一次是函数实现。在编辑器的代码提示中,可以正确的看到前两个提示。
注意,TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
网友评论