首先介绍一下什么是TypeScript ,与JavaScript的区别,及优缺点javascript
首先JavaScript 是一门很是灵活的编程语言:html
TypeScript 的类型系统,在很大程度上弥补了 JavaScript 的缺点。前端
通常来讲,在大型项目中,后期维护成本比前期开发成本要多得多,因此团队规范化尤其重要,包括编码规范,方法调用规范等,而TS能够经过代码的方式,约束团队开发,这样才有利于后期维护及扩展,从而达到高效的开发java
两个最重要的特性——类型系统
、适用于任何规模
。es6
优点:强大的IDE支持,支持类型检测,容许为变量指定类型,语法检测,语法提示web
缺点:有必定的学习成本,须要理解 接口,泛型,类,枚举类型等前端可能不是很熟悉的知识点。typescript
接口(Interfaces):能够用于对``对象的形状Shape`进行描述npm
泛型(Generics):在定义函数,接口或类时,不预先指定具体的类型,而是在使用时在指定类型的一种特性编程
类(Classes):定义了一件事物的抽象特色,包括属性和方法json
若想使用TS进行开发,首先必需要搭建搭建TypeScript开发环境
安装: npm install -g typescript
,全局安装,能够在任意位置执行tsc
版本:tsc -v
编译:tsc 文件名.ts
TS 中,使用:
为变量指定类型,:
先后的空格无关紧要。TS 只会在编译时
对类型进行静态检查
,如发现有错误,编译时就会报错。而在运行时,与普通的 JavaScript 文件同样,不会对类型进行检查。
编译时即便报错,仍是会生成编译结果,仍然可使用编译以后的文件,若想在报错时终止 js文件的生成,能够在 tsconfig.json 中配置 noEmitOnError 便可。
类型系统按照类型检查的时机
分类,能够分为动态类型
和静态类型
。
动态类型
:是指在运行时才会进行类型检查,类型错误每每会致使运行时错误。JavaScript 是一门解释型语言,没有编译阶段,因此它是动态类型,如下代码在运行时才会报错:
// test.js let foo = 1; foo.split(' '); // TypeError: foo.split is not a function 运行时会报错(foo.split 不是一个函数)
静态类型
:是指编译阶段就能肯定每一个变量的类型,类型错误每每会致使语法错误。TypeScript 在运行前须要先编译为 JavaScript,而在编译阶段就会进行类型检查,因此 TypeScript 是静态类型,如下代码在编译阶段就会报错:
// test.ts let foo: number = 1; foo.split(' '); // Property 'split' does not exist on type 'number'. 编译时报错(数字没有 split 方法),没法经过编译
类型系统按照是否容许隐式类型转换
分类,能够分为强类型
和弱类型
。
如下代码在 JS或 TS 中均可以正常运行,运行时数字 1 会被隐式类型转换为字符串 '1',加号 + 被识别为字符串拼接,因此打印出结果是字符串 '11'。
console.log(1 + '1'); // 11
TS 是彻底兼容 JS的,并不会修改 JS 运行时的特性,因此它们都是弱类型
。虽然 TS 不限制加号两侧的类型,可是能够借助类型系统,以及 ESLint 代码检查,来限制加号两侧必须同为数字或同为字符串。会在必定程度上使得 TypeScript 向强类型更近一步了——固然,这种限制是可选的。
这样的类型系统体现了 TypeScript 的核心设计理念:在完整保留 JavaScript 运行时行为的基础上,经过引入静态类型系统来提升代码的可维护性,减小可能出现的 bug。
布尔值、数值、字符串、null、undefined,为变量指定类型,且变量值需与类型一致
let flag: boolean = false let num: number = 15 let str: string = 'abc' var a: null = null var b: undefined = undefined // 编译经过
使用构造函数创造的对象不是原始数据类型,事实上 new XXX() 返回的是一个 XXX对象:
let flag:boolean=new Boolean(false) // Type 'Boolean' is not assignable to type 'boolean'. let num: number = new Number(15) // Type 'Number' is not assignable to type 'number'. let str: string = new String('abc') // Type 'String' is not assignable to type 'string'. // 编译经过
可是能够直接调用 XXX 也能够返回一个 xxx 类型:
let flag: boolean = Boolean(false) let num : number = Number(15) let str : string = String('abc') // 编译经过
使用 number
定义数值类型:
let decLiteral: number = 6; let hexLiteral: number = 0xf00d; let binaryLiteral: number = 0b1010; // ES6 中的二进制表示法 let octalLiteral: number = 0o744; // ES6 中的八进制表示法 let notANumber: number = NaN; let infinityNumber: number = Infinity;
编译结果:
var decLiteral = 6; var hexLiteral = 0xf00d; var binaryLiteral = 10; // ES6 中的二进制表示法 var octalLiteral = 484; // ES6 中的八进制表示法 var notANumber = NaN; var infinityNumber = Infinity;
ES6 中二进制和八进制数值表示法:分别用前缀0b
|0B
和0o
|0O
表示。会被编译为十进制数字。
使用string
定义字符串类型:
let myName: string = 'Echoyya'; let str: string = `Hello, my name is ${myName}.`; // 模板字符串
编译结果:
var myName = 'Echoyya'; var str = "Hello, my name is " + myName + "."; // 模板字符串
ES6 中模板字符串:加强版的字符串,用反引号(`) 标识 ${expr} 用来在模板字符串中嵌入表达式。
JavaScript 没有空值(Void)的概念,在 TS中,用 void 表示没有任何返回值的函数:
function alertName(): void { alert('My name is Tom'); }
然而声明一个 void 类型的变量没什么用,由于只能将其赋值为 undefined 和 null:
let unusable: void = undefined;
Null 和 Undefined
let u: undefined = undefined; let n: null = null;
区别:undefined 和 null 是全部类型的子类型。也就是说 undefined 类型的变量,能够赋值给全部类型的变量,包括 void 类型:
let num: number = undefined; let u: undefined; let str: string = u; let vo: void= u; // 编译经过
而 void 类型的变量不能赋值给其余类型的变量,只能赋值给 void 类型:
let u: void; let num: number = u; // Type 'void' is not assignable to type 'number'. let vo: void = u; // 编译经过
任意值(Any)用来表示容许赋值为任意类型
。一个普通类型,在赋值过程当中是不被容许改变类型的,any 类型,容许被赋值为任意类型。let str: string = 'abc'; str = 123; // Type 'number' is not assignable to type 'string'. // ----------------------------------------------------------------- let str: any = 'abc'; str = 123; // 编译经过
任意值能够访问任意属性和方法:
let anyThing: any = 'hello'; anyThing.setName('Jerry'); anyThing.setName('Jerry').sayHello(); anyThing.myName.setFirstName('Cat'); console.log(anyThing.myName); console.log(anyThing.myName.firstName); // 编译经过
未声明类型的变量
:若是变量在声明时,未指定类型,那么会被识别为任意值类型:let something; something = 'seven'; something = 7; // 等价于 let something: any; something = 'seven'; something = 7;
若未明确指定类型,那么 TS 会依照类型推论(Type Inference)规则
推断出一个类型。
如下代码虽然没有指定类型,但编译时会报错:
let data = 'seven'; data = 7; // Type 'number' is not assignable to type 'string'.
事实上,它等价于:
let data: string = 'seven'; data = 7; // Type 'number' is not assignable to type 'string'.
TS会在未明确指定类型时推测出一个类型,这就是类型推论
。若是定义时未赋值,无论以后是否赋值,都会被推断成 any 类型
:
let data; data = 'seven'; data = 7; // 编译经过
联合类型(Union Types)
表示取值能够为多种类型中的一种
,使用 |
分隔每一个类型。
let data: string | number; // 容许 data 能够是 string 或 number 类型,但不能是其余类型 data = 'seven'; data = 7; data = true; // Type 'boolean' is not assignable to type 'string | number'.
访问联合类型的属性或方法
:当不肯定一个联合类型的变量究竟是哪一个类型时,只能访问此联合类型中全部类型共有的属性或方法:
function getLength(something: string | number): number { return something.length; } // length 不是 string 和 number 的共有属性,因此会报错 // Property 'length' does not exist on type 'string | number'. // Property 'length' does not exist on type 'number'.
访问 string 和 number 的共有属性:
function getString(something: string | number): string { return something.toString(); }
联合类型的变量在被赋值
的时候,会根据类型推论的规则推断出一个类型:
let data: string | number; data = 'seven'; console.log(data.length); // 5 data = 7; console.log(data.length); // 编译时报错 // Property 'length' does not exist on type 'number'.
line2:data 被推断为 string,访问length 属性不会报错。
line4:data 被推断为 number,访问length 属性报错。
在 TS中,使用接口(Interfaces)来定义对象的类型。可用于对类的一部分行为进行抽象
之外,也经常使用于对对象的形状(Shape)
进行描述。
interface Person { name: string; age: number; } let p1: Person = { name: 'Tom', age: 25 };
定义一个接口 Person(接口通常首字母大写),定义一个变量 tom 类型是 Person。这样就约束了 tom 的形状必须和接口 Person 一致。
肯定属性
:赋值时,定义的变量的形状必须与接口形状保持一致。变量的属性比接口少或多
属性都是不容许
的:
interface Person { name: string; age: number; } let p1: Person = { // 缺乏 age 属性 name: 'Tom' }; // Type '{ name: string; }' is not assignable to type 'Person'. // Property 'age' is missing in type '{ name: string; }'. // ----------------------------------------------------------------- let p2 Person = { // 多余 gender 属性 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 p1: Person = { // 编译经过 name: 'Tom' }; let p2: Person = { // 编译经过 name: 'Tom', age: 25 }; let p3: Person = { // 报错(同上) name: 'Tom', age: 25, gender: 'male' };
任意属性
:容许一个接口有任意的属性
interface Person { name: string; age?: number; [propName: string]: any; } let p1: Person = { name: 'Tom', gender: 'male' };
使用 [propName: string]
定义了任意属性
的属性名取 string 类型的值。属性值为任意值
注意:一旦定义了任意属性,那么肯定属性和可选属性的类型都必须是它的类型的子集:
例一:任意属性的类型是 string,可是可选属性 age 的值倒是 number,number 不是 string 的子属性,因此会报错。
interface Person { name: string; age?: number; [propName: string]: string; } let p1: Person = { name: 'Tom', age: 25, gender: 'male' }; // Property 'age' of type 'number' is not assignable to string index type 'string // Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'. // Property 'age' is incompatible with index signature. // Type 'number' is not assignable to type 'string'
例二:一个接口中只能定义一个任意属性。若是接口中有多个类型的属性,能够在任意属性
中使用联合类型
:
interface Person { name: string; age?: number; [propName: string]: string | number; } let p1: Person = { // 编译经过 name: 'Tom', age: 25, gender: 'male', year:2021 };
对象中的一些字段只能在建立时被赋值,可使用 **readonly **定义只读属性
:
例一:使用 readonly 定义的属性 id 初始化后,又被从新赋值,因此会报错。
interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let p1: Person = { id: 89757, name: 'Tom', gender: 'male' }; p1.id = 9527; // Cannot assign to 'id' because it is a read-only property.
例二:只读的约束存在于第一次给对象赋值
的时候,而不是第一次给只读属性赋值时:
interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let p2: Person = { // 第一次给对象赋值 name: 'Tom', gender: 'male' }; p2.id = 89757; // Property 'id' is missing in type '{ name: string; gender: string; }' but required in type 'Person' 对 p2 赋值时,没有给 id 赋值 // Cannot assign to 'id' because it is a read-only property. id 是只读属性
在 TS 中,有多种定义数组类型的方式。
最简单的方法是使用类型 + 方括号
来表示数组:
let arr: number[] = [1, 1, 2]; // 数组元素中不容许出现其余的类型 let arr1: number[] = [1, '1', 2]; // 报错:Type 'string' is not assignable to type 'number'.
数组的一些方法的参数也会根据数组在定义时约定的类型进行限制:
let arr2: number[] = [1, 1, 2, 3, 5]; arr2.push('8'); //报错:Argument of type '"8"' is not assignable to parameter of type 'number'.
使用数组泛型(Array Generic) Array<elemType>
来表示数组:
let arr3: Array<number> = [1, 1, 2, 3, 5];
泛型涉及内容较多,后期有时间会在整理一篇文章,敬请关注!
以前介绍了使用接口表示对象的类型,一样接口也能够用来描述数组:
interface NumberArray { [index: number]: number; } let arr: NumberArray = [1, 1, 2, 3, 5];
NumberArray 表示:索引的类型是数字,值的类型也是数字,这样即可以表示一个数字类型的数组,虽然接口也能够描述数组,可是通常不会这么作,由于这种方式较复杂。有一例外,就是经常使用来表示类数组。
类数组(Array-like Object)不是数组类型,好比 arguments,其实是一个类数组,不能用普通数组的方式来描述,而应该用接口:
function sum() { let args: number[] = arguments; } // 报错:Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
接口描述类数组
:除了约束索引的类型是数字,值的类型也必须是数字以外,也约束了它还有 length 和 callee 两个属性。
function sum() { let args: { [index: number]: number; length: number; callee: Function; } = arguments; }
而事实上经常使用的类数组都有本身的接口定义,如 IArguments, NodeList, HTMLCollection 等:
function sum() { let args: IArguments = arguments; } //其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是: interface IArguments { [index: number]: any; length: number; callee: Function; }
一个比较常见的作法是,用 any 表示数组中容许出现任意类型:
let list: any[] = ['Echoyya', 25, { website: 'https://www.cnblogs.com/echoyya/' }, false];
后续笔记敬请期待~~~