编者荐语: 本文旨在帮助你们在闲暇时间掌握TS用法,故抽出时间,学习整理TypeScript教程,本文大部份内容来自阮一峰老师的网站,你们放心阅读。javascript
如下内容来自于TypeScript 入门教程html
TypeScript
是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。 根据官网翻译成中文是前端
TypeScript 是 JavaScript 的类型的超集,它能够编译成纯 JavaScript。编译出来的 JavaScript 能够运行在任何浏览器上。TypeScript 编译工具能够运行在任何服务器和任何系统上。TypeScript 是开源的。java
TypeScript 官网列举了一些优点,不过阮一峰老师愿意本身总结一下:web
类型推论
TypeScript 的命令行工具安装方法以下:面试
npm install -g typescript
复制代码
以上命令会在全局环境下安装 tsc 命令,安装完成以后,咱们就能够在任何地方执行 tsc 命令了。
编译一个 TypeScript 文件很简单:typescript
tsc hello.ts
复制代码
查看 TypeScript 的版本信息:npm
tsc -v
复制代码
咱们约定使用 TypeScript 编写的文件以 .ts 为后缀,用 TypeScript 编写 React 时,以 .tsx 为后缀。数组
接下来让咱们全面拥抱TypeScript伟大的语言吧~浏览器
JavaScript 的类型分为两种: 原始数据类型和对象类型。
如下部分介绍前五中原始数组类型在TypeScipt中的应用。
在 TypeScript 中,使用 boolean 定义布尔值类型:
let isDone: boolean = false;
// 编译经过
// 后面约定,未强调编译错误的代码片断,默认为编译经过
复制代码
注意,使用构造函数 Boolean 创造的对象不是布尔值:
let createdByNewBoolean: boolean = new Boolean(1);
// Type 'Boolean' is not assignable to type 'boolean'.
复制代码
事实上 new Boolean() 返回的是一个 Boolean 对象
:
let createdByNewBoolean: Boolean = new Boolean(1);
复制代码
在 TypeScript 中,boolean 是 JavaScript 中的基本类型,而
Boolean
是 JavaScript 中的构造函数
。其余基本类型(除了 null 和 undefined)同样,再也不描述。
使用 number 定义数值类型:
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二进制表示法
let binaryLiteral: number = 0b1010;
// ES6 中的八进制表示法
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
复制代码
编译结果:
var decLiteral = 6;
var hexLiteral = 0xf00d;
// ES6 中的二进制表示法
var binaryLiteral = 10;
// ES6 中的八进制表示法
var octalLiteral = 484;
var notANumber = NaN;
复制代码
其中 0b1010 和 0o744 是 ES6 中的 二进制
和 八进制
表示法,它们会被编译为十进制数字。
使用 string 定义字符串类型:
let myName: string = 'Tom';
let myAge: number = 25;
// 模板字符串
let sentence: string = `Hello, my name is ${myName}. I'll be ${myAge + 1} years old next month.`;
复制代码
JavaScript 没有空值(Void)的概念,在 TypeScript 中,能够用 void 表示没有任何返回值的函数:
function alertName(): void {
alert('My name is Tom');
}
复制代码
声明一个 void
类型的变量没有什么用,由于你只能将它赋值为 undefined
和 null
:
let unusable: void = undefined;
复制代码
如下的两点Null和Undefined尤其须要注意一下,由于在js面试中也常问到。
在 TypeScript 中,可使用 null 和 undefined 来定义这两个原始数据类型:
let u: undefined = undefined;
let n: null = null;
复制代码
与
void
的区别是,undefined
和null
是全部类型的子类型
。也就是说undefined
类型的变量,能够赋值给number
类型的变量:
// 这样不会报错
let num: number = undefined;
// 这样也不会报错
let u:undefined;
let num:number = u;
复制代码
而
void
类型的变量不能赋值给number
类型的变量,会报错
let u: void;
let num: number = u;
// Type 'void' is not assignable to type 'number'.
复制代码
任意值(Any)用来表示容许赋值为任意类型。
若是是一个普通类型,在赋值过程当中改变类型是不被容许的:
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;
// 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');
复制代码
任意值必定要慎用,由于在声明any以后。它的任何操做,返回的类型均是 any 对变量类型的检查变为毫无心义,同时这也就失去了TypeScripe强类型检查语言的意义了。
模块的引入多是由第三方库引进,没法确认变量的类型时候,应该用 any(不肯定性类型) - 容许赋值为任意类型
let notSure: any = 4
notSure = 'maybe it is a string'
notSure = true
notSure.myName
notSure.getName() // 以上不会报任何错
复制代码
接下来说一下TypeScript与JavaScript不一样的概念
联合类型表示取值能够为多种类型中的一种。
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
复制代码
let myFavoriteNumber: string | number;
myFavoriteNumber = true;
// 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 不肯定一个联合类型的变量究竟是哪一个类型的时候,咱们<b>只能访问此联合类型的全部类型里共有的属性或方法:</b>
function getLength(something: string | number): number {
return something.length;
}
复制代码
function getLength(something: string | number): number {
return something.length;
}
// 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); // 5
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // 编译时报错
// Property 'length' does not exist on type 'number'.
复制代码
上例中,第二行的 myFavoriteNumber 被推断成了 string
,访问它的 length
属性不会报错。
而第四行的 myFavoriteNumber 被推断成了 number
,访问它的 length
属性时就报错了。
在 TypeScript 中,咱们使用接口 (Interfaces)
来定义对象的类型。
在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动须要由类(classes)去实现(implement)。
TypeScript 中的接口是一个很是灵活的概念,除了可用于 对类的一部分行为进行抽象
之外,也经常使用于对「对象的形状(Shape)」进行描述。
这里注意接口里面定义的对象要用“;”表示,与JS不一样
interface Person {
name: string;
age: number;
}
let tom:Person = {
name: 'Tom',
age: 25
}
复制代码
上面的例子中,咱们定义了一个接口 Person
,接着定义了一个变量 tom
,它的类型是 Person
。这样,咱们就约束了 tom
的形状必须和接口 Person 一致。
接口通常大写,如 Person
定义的变量比接口少了一些属性是不容许的:
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom'
};
// Type '{ name: string; }' is not assignable to type 'Person'.
// Property 'age' is missing in type '{ name: string; }'.
复制代码
定义的变量比接口多一些属性也是不容许的:
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
// Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
复制代码
有时咱们但愿不要彻底匹配一个形状,那么能够用可选属性:
interface Person {
name: string;
age?: number;
}
let tom:Person = {
name: 'Tom'
}
复制代码
可选属性的含义就是该属性能够不存在。
这是仍然不容许添加未定义的属性
interface Person {
name: string;
age?: number;
}
let tom:Person = {
name: 'Tom',
age: 25,
gender: 'male'
}
// Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
复制代码
有时候咱们但愿一个接口容许有任意的属性,可使用以下方式:
interface Person {
name: string;
age?: number;
[propName: string]: any
}
let tom: Person = {
name: 'Tom',
gender: 'male'
}
复制代码
使用 [propName: string]
定义了任意属性取 string
类型的值。
须要注意的是,一旦定义了任意属性,那么肯定属性和可选属性的类型都必须是它的类型的子集
错误写法:
interface Person {
name: string;
age?: number;
[propName: string]: string;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
复制代码
上例中,任意属性的值容许是 string
,可是可选属性 age 的值倒是 number
, number
不是 string
的子属性,因此报错了。
一个接口中只能定义一个任意属性。若是接口中有多个类型的属性,则能够在任意属性中使用联合类型:
正确写法:
interface Person {
name: string;
age?: number;
[propName: string]: string | number;
}
let tom:Person = {
name: 'Tom',
age: 25,
gender: 'male'
}
复制代码
有时候咱们但愿对象中的一些字段只能在建立的时候被赋值,那么能够用 readonly
定义只读属性:
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527;
// Cannot assign to 'id' because it is a constant or a read-only property.
复制代码
上例中,使用 readonly
定义的属性 id
初始化后, 又被赋值
了,因此报错了。
注意,只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候:
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
tom.id = 89757;
// 两处报错
复制代码
报错信息有两处:
只读属性
,因此报错了。在 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"
是一个字符串字面量
类型,会在后续章节中详细介绍。
interface NumberArray {
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
复制代码
NumberArray
表示:只要索引的类型是数字时,那么值的类型必须是数字。
虽然接口也能够用来描述数组,可是咱们通常不会这么作,由于这种方式比前两种方式复杂多了。
不过有一种状况例外,那就是它经常使用来表示类数组。
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;
}
复制代码
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
复制代码
一个比较常见的作法是,用 any
表示数组中容许出现任意类型:
let list: any[] = ['abcd', 25, { website: 'http://xcatliu.com' }]
复制代码
在 JavaScript 中,有两种常见的定义函数的方式——函数声明和函数表达式:
// 函数声明
function sum(x,y) {
return x + y;
}
// 函数表达式
let mySum = function(x, y) {
return x + y;
}
复制代码
限制了参数的类型 和 返回值的 类型
function sum(x: number, y:number):number {
return x + y;
}
复制代码
注意,输入多余的(或者少于要求的)参数,是不被容许的:
function sum(x: number, y: number): number {
return x + y;
}
sum(1, 2, 3);
function sum(x: number, y: number): number {
return x + y;
}
sum(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');
// A required parameter cannot follow an optional parameter.
复制代码
TypeScript 会将添加了默认值的参数识别为可选参数: 不传的话,默认值为 Cat
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');
复制代码
items 是一个数组。因此咱们能够用数组的类型来定义它:
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
复制代码
经过对函数声明式学习的了解,咱们可能会写成这样:
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 的类型定义中,
=> 用来表示函数的定义
,左边是输入类型
,须要用括号
括起来,右边是输出类型
。
咱们也可使用接口的方式
来定义一个函数(参数
)(输出值
)须要符合的形状:
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return souce.search(subString) !== -1;
}
复制代码
采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,能够保证之后对函数名赋值时保证参数个数、参数类型、返回值类型不变。
若是你以为这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
也能够来个人我的博客:
前端时光屋:www.javascriptlab.top/