前言
对于typescript而言如果想要能在项目中熟练的应用,个人认为需要详细去了解以下几点内容:类型(基本类型,联合类型,类型断言,泛型),模块和命名空间,接口和类
基础篇
开发环境
- 编译包全局安装
npm install -g typescript
- 编译ts文件命令
tsc + ts文件
- 监听文件变化并编译
tsc -w + ts文件
基础类型
- typescript基本类型有:nubmer string boolean enum 数组 元组 void null undefined never any,以下是基础类型定义的方法
//number let num: number = 1; console.log('num: ', num); //string let str: string = "string"; console.log('str: ', str); //boolean let bool: boolean = true; console.log('bool: ', bool); //enum表示枚举 enum Color{Red,Blue,White,Black} let enumVal:Color = Color.Black; console.log('enumVal: ', enumVal); //数组 let arr:number[] = [0,1]; arr.push(2) console.log('arr: ', arr); //元组 let tup:[string,number]; tup = ['arr',2]; console.log('tup: ', tup); //void用来表示函数无返回值类型; function hello(): void { console.log("Hello Runoob"); } hello() 复制代码
- 联合类型,我们使用js时候不用考虑数据类型,但是用ts如果定义的类型不能满足实际赋值则会报错,即我们定义了number,但是使用是可能赋值string,此时就需要用到联合类型了
//联合类型 let multiType: string | number | null = 'multi' multiType = 333; console.log('multiType: ', multiType);
类型断言
typescript具备类型推断的能力,当推断的类型满足不了我们需求时候,我们也可以自己定义类型来覆盖typescript的推断,这种机制在typescript中称为类型断言。typescript类型断言有两种方式一种是使用as关键字进行,另外一种是使用<>符号。
- as断言应用如下,假设我们取id为input的输入的值,直接取会报错,此时可以用到类型断言来处理,具体代码如下:
const inputEle:HTMLElement = document.querySelector("#input")!; const value = inputEle.value # 类型“HTMLElement”上不存在属性“value”。ts(2339) const val = (inputEle as HTMLInputElement).value
- 断言
const inputEle:HTMLElement = document.querySelector("#input")!; const value = inputEle.value # 类型“HTMLElement”上不存在属性“value”。ts(2339) const val = (<HTMLInputElement>inputEle).value
接口和类
TypeScript的核心原则之一是对值所具有的结构进行类型检查。 它有时被称做“鸭式辨型法”或“结构性子类型化”。 在TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约,需要注意的是接口不能转换成JavaScript,他只是Typescript的一部分。
- 普通接口
// demo/interface.ts定义 export interface PersonProps{ name:string; age:number; height:number; weight:number; }
import {PersonProps} from './demo/interface'; const studentA:PersonProps = { name:"xiaomin", age:10, } #Type '{ name: string; age: number; }' is missing the following properties from type #'PersonProps': height, #weightts(2739),当定义的变量不满足接口定义的属性时会报错,在属性中加上weight:80,height: #178即可
- 联合类型接口:接口中的属性也可以定义多个类型
// demo/interface.ts定义 export interface StutentProps{ book:string[]|string|null; grade:number; }
const studentC:StudentProps = { book:['math','English'], grade: 333 } const studentD:StudentProps = { book: "Chinese", grade: 333 }
- 接口和数组:接口中我们可以将数组的索引值和元素设置为不同类型,索引值可以是数字或字符串。这种描述数组的方式比较复杂我们尽量用字面量的形式来描述。
// demo/interface.ts定义 export interface EmployeeProps{ [index:number]:string; }
import {PersonProps,EmployeeProps} from './demo/interface'; const employeeList:EmployeeProps = ['employee1','employee2',2]
- 继承接口:接口是可以继承其他接口的,继承后
export interface TeacherProps extends PersonProps{ student: string | string[] | null; courseType: string; }
import {PersonProps,StudentProps,TeacherProps,EmployeeProps} from './demo/interface'; const teacherA:TeacherProps = { student:'a', courseType: 'chinese', age:33, weight: 60, height:183, name:'teacher' }
- 类的使用,定义类的关键字为 class,后面紧跟类名,类可以包含以下几个模块(类的数据成员):字段(字段是类里面声明的变量。字段表示对象的有关数据。),构造函数(类实例化时调用,可以为类的对象分配内存),方法( 方法为对象要执行的操作)
//定义类名称 class Person { name: string; age: number; //定义构造函数 constructor(name:string,age:number){ this.name = name; this.age = age; } setAge(age: number) { this.age = age; } setName(name:string){ this.name = name; } } const person = new Person('ts',100); person.setName('this is a ts') console.log('person: ', person); #输入 person: Person { name: 'this is a ts', age: 100 }
泛型的使用
固定类型的定义解决了在大型多人项目中js动态类型难以维护的问题,但在软件设计中重用性也是软件基础能力之一,typescript泛型就是为了解决此问题。
模块与命名空间
- 命名空间:在ECMAScript 2015之前,JavaScript语言没有内置的模块支持。在JavaScript程序中,通常使用“命名空间”来组织并隔离代码以免产生命名冲突等问题。JavaScript中的命名空间是以自执行函数来实现,Typescript利用了这个方式来提供了命名空间声明的简便语法- namespace
//定义的ts文件 namespace Utils{ const name = "demo" const setName = (n:string)=>{ console.log('======== set name ======'); } } //编译后的文件 "use strict"; var Utils; (function (Utils) { const name = "demo"; const setName = (n) => { console.log('======== set name ======'); }; })(Utils || (Utils = {}));
//外部引用命名空间内部的变量需要使用export关键字导出 export namespace Utils{ const name = "demo"; export const setName = (n:string)=>{ console.log('======== set name ======'); } export const getName = ()=>{ return name; } } const name = Utils.getName(); //导出的编译会将其挂载到命名空间的对象上如下: (function (Utils) { const name = "demo"; Utils.setName = (n) => { console.log('======== set name ======'); }; Utils.getName = () => { return name; }; })(Utils = exports.Utils || (exports.Utils = {}));
- 命名空间中.d.ts的作用:用户JavaScript项typescript的过渡,为 TypeScript 提供有关用 JavaScript 编写的 API 的类型信息。 注:命名空间主要解决的问题:解决同一个文件中命名重复的问题。但是随着JavaScript模块系统的演进以及原生模块功能的推出,命名空间的使用场景将变得越来越有限,后续推荐大家使用魔窟来编程。对于命名空间我们只是做一个了解即可。
- 模块:在ECMAScript 2015统一模块定义之前,JavaScript就有自发的几种定义模块的方式, CommonJS(nodejs的模块规范),AMD(浏览器的模块规范),UMD(统一的模)
- 模块导入导出:在ts中定义的模块可以相互引用,可以通过export导出,import引入
// util.ts 声明导出的模块 const formatName = (name:string)=>{ name = 'this is a name:' + name; return name } export {formatName} // 导入模块中的导出内容 import {formatName} from './utils'; const name = 'name'; const newName = formatName(name);