1,什么是typescript?
官方定义ts是js的超集,反之js就是ts的子集;所以ts包含很多js没有的能力,弥补了js弱类型的不方便,增强了面向对象的能力,等于js的扩展。但是顺带一提,ts最终编译的结果仍然是js。
2,ts增加了哪些能力?
ts的数据类型可以定义从简单到复杂的一切类型。
增加了一些新的概念:包括接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)、类的访问修饰符(public)
ts核心库不包含node.js
2,typescript的应用场景
增强针对js的代码补全、接口提示、跳转到定义、重构等功能:在node_moudles中的@types中的index.d.ts就是ts的类型声明文件,它为ts的类型检查提供了有力支持,使用npm
install
@types
/sequelize
--save-dev安装,在vue项目中包含有,用来检测js语法规范
3,typescript如何使用
(假如项目中没有webpack,需要 安装编译工具):npm install -g typescript,将ts编译为js:tsc hello.ts
5,ts的语法规则
数据类型
原始数据类型:boolean、number、string、null、undifined、void、以及 ES6 中的新类型 Symbol 和 BigInt
对象类型:数组、函数、对象、类、Boolean、Error、Date......包括js中定义好的所有对象以及DOM和BOM对象
任意类型:any
注意一:undefined和null是所有类型的子类型,可以赋值给任意类型的变量
定义变量
使用 :
指定变量的类型,:
的前后有没有空格都可以,例如name: string,其中的name表示变量名,string表示变量类型是string字符串类型。ts的类型包含所有js中定义好的更多对象,例如:
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
也包含DOM和BOM的内置对象,例如:
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
});
ts中的数据类型中包含任意类型any,一般被定义为任意类型的变量,可以访问任何属性和方法(即使不存在),且返回值都是任意类型
类型推论
定义变量的时候如果没有指定类型且没有赋值,就会默认为any类型,后续赋值为任何类型都不会报错,但假设定义变量的时候赋值了,就会默认该变量为初始值类型,后续更改为其他类型值会报错:
let person; //默认为any类型
person = “bob”;
person = 13;
let son = "amy" ; //默认类型为string
son = 12 ; //error 报错
报错位置
假如ts的变量类型定义出错,在编译阶段会报错并能生成js文件,一旦编译为js后就不会报错了
文件后缀
使用ts编写的文件后缀为ts,使用ts编写的react后缀为tsx
引用第三方库
库的使用场景主要有以下几种:
- 全局变量:通过
<script>
标签引入第三方库,注入全局变量 - npm 包:通过
import foo from 'foo'
导入,符合 ES6 模块规范 - UMD 库:既可以通过
<script>
标签引入,又可以通过import
导入 - 直接扩展全局变量:通过
<script>
标签引入后,改变一个全局变量的结构 - 在 npm 包或 UMD 库中扩展全局变量:引用 npm 包或 UMD 库后,改变一个全局变量的结构
- 模块插件:通过
<script>
或import
导入后,改变另一个模块的结构
使用第三方库,必须引入声明文件(例如jquery.d.ts)才有代码补全、接口提示功能,整个项目只要有一处引入即可;
情况一:作者自己写了声明文件,npm包和声明文件绑定,不需要操作:npm install jquery
情况二: 作者没有写声明文件,但是有人帮忙发布到@types中:npm install @types/jquery --save-dev
情况三: 作者没有写声明文件,@types也没有,npm install jquery 然后自己写声明文件 <reference path="jQuery.d.ts"/>,并且配置文件tsconfig.json:
/path/to/project
├── src
| └── index.ts
├── types
| └── foo
| └── index.d.ts
└── tsconfig.json
//tsconfig.json内容:
{
"compilerOptions": {
"module": "commonjs",
"baseUrl": "./",
"paths": {
"*": ["types/*"]
}
}
}
编写声明文件之全局变量
全局变量的声明文件主要有以下几种语法:
- declare var 声明全局变量
- declare function 声明全局方法
- declare class 声明全局类
- declare enum 声明全局枚举类型
- declare namespace 声明(含有子属性的)全局对象
- interface 和 type 声明全局类型
类型断言
类型断言的意思是,先假设这个变量属于某个类型,以暂时获得该类型下的属性调用权力,防止编译报错
解决问题一:将一个联合类型断言为其中一个类型
比如假设变量A是string类型就执行代码A,假设变量A是number类型就执行代码B就可以用到类型断言:
if(A as string) {
//do some
}
解决问题二:将一个父类断言为更加具体的子类
假设变量A是某个class或者interface 的子类,临时获取它们的属性值
class ApiError extends Error {
code: number = 0;
}
function isApiError(error: Error) {
if (typeof (error as ApiError).code === 'number') {
return true;
}
}
解决问题三:将任何一个类型断言为 any
使用window定义全局变量:window.foo=1;由于window上没有属性foo会导致不必要的报错,此时使用类型断言:(window as any).foo=1即可;
解决问题四:将any类型断言为一个具体的类型
断言的限制
- 要使得
A
能够被断言为B
,只需要A
兼容B
或B
兼容A
即可
所谓的兼容,指的是A与B具有父子关系
双重断言
为了解决这种限制,出现了双重断言:即A与B不具有父子关系也能够互相进行类型断言,断言方式 :A as any as B
函数的定义
函数声明
function sum(x:number):number{
return x
}
函数表达式
let sum(x:number)=>number = function(x:number):number{
return x
}
可选参数
一般函数要求实参的个数和形参个数相同,针对可选的参数则使用?进行定义,且必须放在参数列表的末尾:
function sun(x:number, y?:number){ }
参数默认值:
function sum (x:number, y: number=1){ }
剩余参数:
剩余参数的类型实际上 是数组,直接如下定义即可
function sum (x:number, ...items: any[]){ }
箭头符号
接口
接口是属性和抽象方法的集合,具体方法的实现由类完成。命名要求首字母大写。ts使用接口定义对象的类型 ,且对象实例化接口时,属性需要完全与接口一致,不能减少,除非使用可选属性:也不能增加,除非使用任意属性,任意属性只能定义一个(且其他确定属性和可选属性必须是它的子集,所以任意属性一般是联合类型);
interface Interface_name{
readonly name: string, //只读属性,只能在对象被创建的时候赋值
getAge:()=>string, //确定属性
height?:number , //表示可选属性
[propName: string]:any, //任意属性,表示属性取string的任意类型
[propName: string]:string | number, //任意属性,表示属性取string的string或者number类型
}
let classA:Interface ={
name:"bib",
getAge: ():string=>{ return 3},
fat: "yes"
单接口继承:
接口A extends 接口B
多接口继承:
接口A extends 接口B , 接口C
数组
方式一:let age:number[ ];
方式二(数组泛型) let age: Array<number>
方式三(接口表示) interface A{
[index:string]:number;
}
let arrs: A = [1,2,3]
接口和数组
interface Arrs{
[index: string]:number
}
let aaa: Arrs;
aaa["age"]= 16;
类
类包含三个成员,字段name,构造函数以及方法
class A{
name:string;
constructor(newname:string){
this.name = newname;
}
getname:void{
console.log( 'go')
}
}
类的修饰符
public: 公有,访问不受限制
protected: 仅限于本类和子类访问
private: 仅限于本类访问
readonly: 只读,只能修饰属性声明,构造函数的形参
注意一:constructor被修饰为private,该类不能被继承和实例化;constructor被修饰为protected,该类不能被实例化;
注意二:readonly必须在修饰符后面,比如 public readonly name
抽象类
使用abstract修饰的类,用于定义抽象函数,不允许被实例化,子类必须实现其抽象方法:
abstract class A{
public name;
constractor(){}
abstract getname()
}
枚举enum
enum people{ bob, amy, jerry, jack}
自动赋值:成员bob等按照从0升序赋值,跨度为1
枚举值和枚举名反向映射:people["bob"] //得0, people[0] //得bob
手动赋值: enum people{ bob=100,amy,jerry,jack}
计算所得项:enum people{ bob, amy, jerry="java".length}
常数枚举:const enum people{ bob, amy, jerry} ,常数枚举不能包含计算所得项
keyof
是个类型运算符,针对对象,可以读取对象的键。
场景:实现Pick函数
type Pick<T,P extends keyof T> = {
[K in P]:T[K]
}
映射类型[ ]:string
场景:表示类型A里面所有属性的key都是string,value都是boolean
type A = {
[key:string]: boolean
}
映射修改器+-
type A={
-readonly[key:string]-?: boolean; //去掉readonly和可选属性
}
键取别名as
type A<T> = {
[K in T as `get${K}`]:boolean
}
内置函数
Omit<T,"name" |"age">:除了name/age以外的属性都保留
Pick<T, "name | "age">:只要name/age
Exclude<T, "name">:除了name以外都其他值(T必须是联合类型)
Capitalize<“hello">:将首字母大写Hello