关于TypeScript
TypeScript
是 JavaScript
的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
TypeScript优缺点
- TypeScript 增加了代码的可读性和可维护性
-
TypeScript 非常包容
typeScript 是 JavaScript 的超集,
.js
文件可以直接重命名为.ts
即可
即使不显式的定义类型,也能够自动做出类型推论
可以定义从简单到复杂的几乎一切类型
即使 TypeScript 编译报错,也可以生成 JavaScript 文件
兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取 - 有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念,可能会增加开发成本
快速上手
命令行安装:
npm install -g typescript
hello world ,新建index.ts
文件:
function sayHello(person: string) {
return 'Hello, ' + person;
}
let user = 'Tom';
console.log(sayHello(user));
运行:
tsc index.ts
TypeScript 中,使用 :
指定变量的类型,:
的前后有没有空格都可以。
TypeScript 只会进行静态检查,如果发现有错误,编译的时候就会报错。
但还是会生成对应的.js
文件,如果要在报错的时候终止 js 文件的生成,可以在 tsconfig.json
中配置 noEmitOnError
即可。关于配置项
基础
JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。
原始数据类型
原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol。
指定这些变量类型:
let isShow: boolean = false; //布尔值 new Boolean(true) 不可 new出来是对象 可以Boolean()
let name: string = '君莫笑'; //字符串
let age: number = 5; //数值
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d; //hex 十六进制
let binaryLiteral: number = 0b1010; //binary 二进制
let octalLiteral: number = 0o744; //octal 八进制 OCT
let notANumber: number = NaN;
let infinityNumber: number = Infinity;
let u: undefined = undefined;
let n: null = null;
// null和undefined是所有类型的子类型,也就是说其他类型可以设值为null或undefined(void除外)
let account: string = u;
JavaScript 没有空值(Void)
的概念,在 TypeScript
中,可以用 void
表示没有任何返回值的函数:
function alertName(): void {
alert('My name is Tom');
}
声明一个 void
类型的变量没有什么用,因为你只能将它赋值为 undefined
和 null
:
let unusable: void = undefined;
任意值, 用any
代替 : let sth: any = null
;
类型推论
其实就是根据之前定义时的初值类型推断出变量类型,若没有赋值,则识别为any
,如以下代码会报错:
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;
联合类型
联合类型(Union Types)
表示取值可以为多种类型中的一种,使用 |
分隔每个类型
let myFavoriteNumber: string | number
//允许 myFavoriteNumber 的类型是 string 或者 number,但是不能是其他类型
当 TypeScript
不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法:
function getMyValue(val: string | number ) {
// return val.length; //报错
return val.toString();
}
当变量赋值时,TypeScript
会根据类型推论推断变量类型并调用相应的方法属性等:
myFavoriteNumber = 'number';
console.log('myFavoriteNumber: ', myFavoriteNumber.length);
myFavoriteNumber = 7;
// console.log('myFavoriteNumber: ', myFavoriteNumber.length); //报错
对象的类型——接口
TypeScript
中,使用接口(Interfaces)
来定义对象的类型。
在面向对象语言中,接口(Interfaces
)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)
去实现(implement)
。TypeScript
中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。
接口一般首字母大写。有的编程语言中会建议接口的名称加上 I
前缀。
- 赋值的时候,变量的形状必须和接口的形状保持一致,多了属性和少了属性都会错误提示;
-
可选属性:如果不想要完全匹配一个形状,那么可以用可选属性,在属性后加
?
表示该属性在实例化时可有可无,可之后据情况添加;以上两种情况仍然不允许添加未定义的属性。
-
未定义属性:使用
[propName: string]
定义了任意string
类型属性(只能是string
或number
)的值。 需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:也就是说任意属性(若干个)的类型必须是声明属性的超集,任意类型必须包括已声明的所有类型包括可选属性。 -
只读属性: 可以用 readonly 定义只读属性,即希望对象中的一些字段只能在创建的时候被赋值。
注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候
interface IStudent {
readonly id?: number,
name: string;
age: number;
school: string;
punishment?: boolean; //可选属性
[propName: string]: any; //一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
}
let Jack: IStudent = {
// id: 410184,
name: 'jack',
age: 18,
school: '浙江大学',
};
// Jack.id = 5; //只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候报错
Jack.punishment = true;
Jack.dad = 'zhanghao';
console.log(Jack);
数组类型
-
「类型 + 方括号」表示法
let fibonacci: number[] = [1, 1, 2, 3, 5]; //出现其他类型报错,push其他类型也会报错 let arrAny: any[] = [ 3, 'zh', { name: 'zh' } ]; //任意类型的数组
-
用数组泛型(Array Generic)
Array<elemType>
来表示数组,关于泛型let arrNum: Array<number> = [1, 2, 3];
- 使用接口表示数组,其实只是构造了一个和数组结构差不多的对象。
interface INumberArray {
[index: number]: number;
}
let fibonacci: INumberArray = [1, 1, 2, 3, 5];
let testObj: INumberArray = {
1: 5,
3: 6
}; //都不会报错
console.log(testObj.length); //报错
4.类数组
interface IArguments {
[index: number]: any; //
length: number;
}
function sum(a: number, b: number, c: number) {
let args: IArguments = arguments;
console.log(args);
}
sum(3, 9, 18);//3 9 18
以上是我期待的结果,但是报错数字索引签名重复, 打印 agruments: [Arguments] { '0': 3, '1': 9, '2': 18 }
,看到键值是字符串,改IArguments
接口 为
interface IArguments {
[index: string]: any;
length: number;
}
运行得预期。
函数类型
一般的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;
};
而不是
let mySum = function (x: number, y: number): number {
return x + y;
};
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型
注意,输入多余的(或者少于要求的)参数,是不被允许的,
可选参数:可在参数后加?
表示可选参数,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了。
默认参数: 与js类似,不过TypeScript 会将添加了默认值的参数识别为可选参数。
function buildName(firstName: string = 'Tom', lastName?: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
剩余参数: 必须是最后一个参数。
重载: 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
比如,我们需要实现一个函数 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 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。