TypeScript
javaScript的超集
TypeScript = type + javaScript 在js的基础上增加了类型的支持
官方地址:www.tslang.cn/docs/handbo…
安装
npm install -g typescript
查看版本
npm view typescript version 所有可使用的版本 npm view typescript versions
查看本地是否安装
npm ls typescript 查看全局是否安装 npm ls typescript -g
卸载
npm uninstall typescript
编译命令
tsc + 文件名 即可生成对应的js文件
开发商
微软
增加类型的原因
- js的错误大部分都是类型报错 Uncaught TypeError js不会检查变量类型的变化Ts会
- TS属于静态类型编程语言(编译期间做检查) Js属于动态类型编程语言(执行期间做检查)
所以TS可以在编译的时候就发现代码的错误,提前解决
简化执行流程
ts文件需要先编译为js文件,然后通过nodejs进行执行, 可以通过插件 ts-node 直接执行ts文件 插件 ts-node npm install -g ts-node 执行命令 ts-node + 文件名
类型注解
变量名称后用来标记变量类型的代码
常用类型
JS 原始类型 number bigInt string boolean null undefine symbol(es6新增) JS 对象类型 object array function TS 1.联合类型 2.自定义类型 3.接口 4.元祖 5.字面量 6.枚举 7.void 8.any 等 基础类型还有其他高级类型
Ts中原始类型
六种完全按照js中的写法 number string boolean null undefined symbol
let decLiteral: number = 6;
let name: string = "bob";
let isDone: boolean = false;
let sentence: string = `Hello, my name is ${ name }.`
let s:symbol = Symbol()
ts中的对象类型 每个具体的类型都有自己的类型语法
数组类型
let list: number[] = [1, 2, 3]; let list: Array<number> = [1, 2, 3];
let list: boolean[] = [true, false, true]; let list: Array<boolean> = [true, false, true];
let list: string[] = ['1','2','3'] let list: Array<string> = ['1','2','3']
Array<number>中的 number可以根据你想要使用的数组数据类型进行调整
联合类型
由一个或者多个类型组合而成的类型,对于数组而言可以解决一个数组中只能出现一种类型的问题
let arr:(number|string)[] = ["12",1]
let arr:Array<number|string> = ["12",1];
对比区分 let arr:number|string[] 表示既可以是number 类型的 也可以是string类型的数组
类型别名 也叫自定义类型
类型别名是自定义的类型,就是为任意类型起一个别名 使用场景: 定义一个较为复杂的类型并且这个类型经常会被到处使用。重复的复制同一个声明太麻烦,起一个名字直接用简短的名字表示 使用位置: 类型注解的位置 起名规则: type:标志 类型名称只要是合法变量名称即可 大写开头和小写开头都可以
// type customerArray = (number|string)[]
type customerArray = Array<number|string>;
let arrx:customerArray = ["123",1];
函数类型
主要用来控制函数的参数类型和返回类型,可以分为两类 1.单独指定参数和返回值的类型 2. 同时指定参数和返回值的类型
const 函数名称 = ()=>{
常规函数
}
1.单独指定 的两种函数声明方式 ----传参处指定
函数声明
function add(num: number, num1: number): number {
return num + num1
}
函数表达式
const add1 = (num: number, num1: number): number=>{
return num + num1
}
const 函数名称 = (参数1:类型注解1,参数2,类型注解2):返回值注解 =>{
在函数传参中写注解并在后面附返回值注解 用:连接
}
2. 同时指定参数和返回值类型 ---只适用于函数表达式 不适用于函数声明 --------- 名称处指定
const add2: (num: number, num1: number) => number = (num, num1) =>{
return num + num1
}
const 函数名称:(参数1:类型注解1,参数2,类型注解2)=>返回值注解 = (参数1,参数1)=>{
在名称之后接写完参数和返回值注解 参数注解和返回值注解之间用 => 连接
}
void 类型
函数类型的一种 没有返回值的函数
const add1 = (num: number, num1: number): void => {
console.log(1)
}
函数可选参数
函数中有的参数即可一传递又可以不传递 在这种情况下就需要可选参数
在参数后面加一个问号,并且可选参数只能出现在参数排序位置的后面
function add(num: number, num1?: number): void {
console.log(num);
console.log(num1);
}
对象类型
原对象
let person = {
name: "jack",
age: 18,
eat(){
console.log("eat");
}
}
TS对象
let person:{
name: string;
age: number;
eat(food:string,food2:string):void
} = {
name: "jack",
age: 18,
eat(food,food2){
console.log("eat");
}
}
其中 eat(food:string,food2:string):void 可以写为 eat:(food:string,food2:string)=>void
另外 person:{
name: string
age: number
eat(food:string,food2:string):void
}
分行写的时候可以去掉; 一行的时候不能省略person:{name: string;age: number;eat(food:string,food2:string):void}
对象的可选属性
需要加一个?类似函数可选参数
person:{
name?: string
age: number
eat(food:string,food2:string):void
}
接口类型 interface
使用场景: 当一个类型的对象经常被使用,会有重复性的代码 类似于 类型别名 和接口类型有相似的地方也有不同的地方
let person:{
name: string;
age: number;
eat(food:string,food2?:string):void
} = {
name: "jack",
age: 18,
eat(food,food2){
console.log("eat");
console.log(food);
console.log(food2);
}
}
let person1:{
name: string;
age: number;
eat(food:string,food2?:string):void
} = {
name: "jack",
age: 18,
eat(food,food2){
console.log("eat");
console.log(food);
console.log(food2);
}
}
// 此时如果需要声明一个和person相同的对象需要重复以上的代码 很繁琐
// 使用接口
interface Person {
name: string;
age: number;
eat(food:string,food2?:string):void
}
let person2:Person = {
name: "jack",
age: 18,
eat(food,food2){
console.log("eat");
console.log(food);
console.log(food2);
}
}
let person3:Person = {
name: "jack",
age: 18,
eat(food,food2){
console.log("eat");
console.log(food);
console.log(food2);
}
}
接口和类型别名的对比
相同: 都可以给对象指定类型 不同点: 使用范围不同,类型别名除了对象以外还可以为任意的类型指定别名,但是接口只能用于对象
// 使用接口
interface Person {
name: string;
age: number;
eat(food:string,food2?:string):void
}
// 使用类型别名
type Person1= {
name: string;
age: number;
eat(food:string,food2?:string):void
}
let person2:Person = {
name: "jack",
age: 18,
eat(food,food2){
console.log("eat");
console.log(food);
console.log(food2);
}
}
let person3:Person1 = {
name: "jack",
age: 18,
eat(food,food2){
console.log("eat");
console.log(food);
console.log(food2);
}
}
接口之间的继承 ?? 扩展
如果两个接口之间有相同的属性或者方法,可以通过抽离公共属性方法,通过继承来实现复用 继承方式: 通过extends 关键字进行 extends 翻译为继承是否准确根据后续的用法 直接用原意扩展反而更合适
interface Point2D {
x:number
y:number
}
interface Point3D {
x:number
y:number
z:number
}
// 使用继承来处理
interface Point2D {
x: number
y: number
}
interface Point3D extends Point2D { z: number }
let ponit3:Point3D = {
x:1,
y:2,
z:3
}
元组类型
应用场景: 某些固定的数据比如经纬度 数组存储只需要两个长度,但是数组却可能出现任意多的元素,因此数组不严谨 元组可以明确的表示包含多少个元素和元素对应的类型
// 数组的定义
let position: Array<number> = []
let position1: number[] = []
// 元组的定义
let position3:[number,string] =[1,"23"]
position3[0] = 2;
console.log(position3)
类型推论
在ts中没有明确声明类型的地方,类型推论机制会帮助提供类型---因为类型推论的存在这些地方的注解是可以不用写的。
1. 声明时省略
// 初始化变量并指定了一个值的时候 类型推论是可以推断变量的类型
let age = 18;
let age1: number = 18;
// age1 = "12" 会报错
let age2;
// 这种声明了但是没有给出值, 变量 "age2" 隐式具有 "any" 类型,但可以从用法中推断出更好的类型。 类型推论给出的结果是any 类型
age2 = 1;
age2 = "1"
2.函数的返回值省略
// 原函数声明
function add(num: number, num1: number): number {
return num + num1
}
// 用类型推断省略 返回值注解
function add1(num: number, num1: number) {
return num + num1
}
推荐: 能省略类型注解的地方就省略类型注解,为了开发效率方便
类型断言
使用场景: 开发人员非常确认一个值的类型,此时可以使用类型断言指定具体的值 a标签举例 const alink = document.getElementById('link'); // link.href 此时是没有值的,提示没有href属性 原因就是 getElementById 返回值是HTMLElement类型,此类型 // 只包含标签的公共的属性和方法,对于a标签的特殊属性是不包含的 因此需要更具体的类型
类型断言方法
1. as
const alink = document.getElementById('link') as HTMLAnchorElement;
2.<> 不常用 react中也不支持
const alink = <HTMLAnchorElement>document.getElementById('link')
技巧: 如何获取元素的类型 控制台打印 console.dir(元素) 即可获取元素类型
console.dir() 是一种在控制台中查看指定 JavaScript 对象的所有属性的方法,开发人员可以通过这种方式轻松获取对象的属性。 https://developer.mozilla.org/zh-CN/docs/Web/API/console/dir
操作: 在element中选中标签 此时该标签即为$0 使用 console.dir($0) 即可查看该元素的所有信息
字面量类型
一般配合联合类型一起使用 使用场景: 用来表示一组明确的可选值列表 这种情况也可以用枚举的方式实现
let str1 = "hellow ts";// 类型为 strign
const str2 = "hellow ts"; // 类型 hellow ts
// 解释: str1是一个变量 值可以是任何的字符串
// str2是一个常量 值不能变化只能是 hellow ts 所以类型就是 hellow ts
1. 例子
一个字面量就是一个类型
function changeDirection(direction:"up"|"down"|"left"|"right"){
console.log(direction)
}
changeDirection("down")
里面的四个方向每一个都是一种类型
2. 例子 通过类型别面 给联合类型命名 使结构清晰
type directionList = "up"|"down"|"left"|"right";
function changeDirection1(direction:directionList){
console.log(direction)
}
changeDirection1("right")
枚举类型
枚举: 定义了一组命名后的常量 访问枚举成员可以通过.来获得类似对象属性
enum direction { up, down, right, left }
function changeDirection(direction: direction) {
console.log(direction)
}
changeDirection(direction.left)
direction.left的值为3
枚举成员的默认的初始化值是数字值,称作数字枚举,我们也可以给枚举成员进行赋值
// 可修改枚举值
enum direction1 { up = "up", down = "down", right = "rihgt", left = 1 }
function changeDirection1(direction: direction1) {
console.log(direction)
}
changeDirection1(direction1.down)
字符串枚举
枚举成员的值都是字符串的叫做字符串枚举 特点: 没有自增长行为,只有数字枚举才有自增长
enum direction1 { up = "up", down = "down", right = "rihgt", left = "left" }
function changeDirection1(direction: direction1) {
console.log(direction)
}
changeDirection1(direction1.down)
枚举的特点原理
特点: 枚举不仅仅是作为类型的存在,枚举因为本身有值的存在,每个成员都有值。其他的类型会在编译为js后,自动移除,但是枚举值会被编译为js代码。
一组明确可选的的值列表,一般推荐使用 字面量类型+联合类型组合的方式实现,不采用枚举。
any类型
一般不推荐any类型,除非临时使用any,避免很长和复杂的类型
隐式具有any类型的情况:
- 声明变量不提供类型也不提供默认值会自动给与隐式any
- 函数参数不加类型的时候也会给与隐式any类型
以上的都不建议
typeof
js中的typeof:获取数据的类型 --此处需要一个区分数据类型的插件 developer.mozilla.org/zh-CN/docs/…
ts中的typeof: 在类型上下文中引用变量或属性的类型 使用场景: 根据已有变量的值,获取变量的类型,简化类型书写
let p = {
x:1,
y:2
}
// 1.基本写法
function format(point:{x:number;y:number}){}
format(p)
// 2. typeof 写法
function format(point: typeof p){}
format(p)
所以区别就是:
在不同环境中typeof 具有不同的效果 (point: typeof p) 类型上下文中获得的结果和普通代码中的console.log(typeof p); 获得结果是不一样的。
即类型注解处一种结果 普通代码中一种结构
typeScript高级类型
- class
- 类型兼容性
- 交叉类型
- 泛型和keyof
- 索引签名类型和索引查询类型
- 映射类型
class
ES2015 中 class是关键字 可以声明类 在TS中依然保留,并且class声明的类可以作为一个类型存在
<!-- class 基本结构 -->
class Person {
age: number
gender = "男"
// gender:string = "nan" 有初始值通过类型推断 省略了类型注解
constructor(age: number, gender: string) {
// 类的构造函数 为实例设置初始值的目的
this.age = age
this.gender = gender
}
};
const p = new Person(18,"女");
console.log(p.age)
构造函数不指定返回值类型,因为实例是person 类型。
<!-- class 实例方法 -->
class Point {
x: number
y: number
constructor(x: number, y: number) {
// 类的构造函数 为实例设置初始值的目的
this.x = x
this.y = y
}
scale(n: number): void {
// 实例方法
this.x *= n;
this.y *= n;
}
};
const p = new Point(5, 2);
p.scale(10)
console.log(p.x)
<!-- class继承 -->
// 类的继承 实现公共属性公共方法的复用
class Animal {
move() {
console.log("跑跑");
}
}
class Dog extends Animal {
name = "erha"
bark() {
console.log("咬人")
}
}
const dog = new Dog()
// console.log(dog.name)
// TS中的继承 1.extends继承父类 2.implements 实现接口(ts中提供的)
// implements 翻译为实现 指的是一个类实现一个接口的
// 接口 类实现接口的时候必须提供接口的方法
interface Singable {
sing(): void
}
class Person implements Singable {
sing(): void {
console.log("xiapingugo")
}
}
let p = new Person();
// p.sing()
<!-- 类成员的可见性 -->
// 类可见性的修饰符 用来控制类的方法属性是不是可以对外部可见 1.public共有的 2. protected受保护的 3.private私有的
class Animal {
public move() {
// 1. 表示此方法任何公有成员都可以访问
console.log("跑跑");
}
protected move1(){
// 2. 只能在当前类和子类中调用,但是在实例对象(不管是animal还是dog的实例对象都不行)上不能调用
// 比如在在子类Dog中可以通过 this.move1()调用
console.log("跑跑1");
this.move2()
}
private move2(){
// 2. 只在当前类中可见 不管是实例对象还是子类和子类的实例对象都不可见
console.log("跑跑2");
}
}
class Dog extends Animal {
name = "erha"
bark() {
console.log("咬人")
this.move1()
}
}
const dog = new Dog()
dog.bark()
<!-- readonly 只读修饰符 -->
// readonly 只读修饰符 用来防止在构造函数之外对属性值进行修改 只能修饰属性不能修饰方法
class Animal {
// 只读修饰符 此时 如果不加:number 则 age的类型注解类型就是 18 加了number 才是number
readonly age:number = 6
constructor(age:number){
// 只读的属性只能在constructor中进行修改
this.age = age
}
}
// 接口或者{}表示的对象类型 也可以使用 readonly
interface IPerson {
readonly name: string
}
let obj :IPerson = {
name: '是名字啊'
}
console.log(obj.name)
// obj.name = "ce" 这种会报错
let aa :{
readonly name: number
} = {
name : 18
}
// aa.name = 19; 此时也会报错
类型兼容性
// 1. 类型兼容性 // 两种类型系统 1. structural type system 结构化类型系统 2. nominal type system 标明类型系统 // TS 也是 结构化类型系统,关注的是值的结构 如果两个值具有相同的结构 则认为是同一类型
class Point {
x:number
y:number
}
class Point3D {
x:number
y:number
z:number
}
// 成员多的可以赋值给成员少的,即少的兼容多的
const pt :Point = new Point3D()
// 1. Point 和 Point2D 是不同的类
// 2. pt 的类型注解是Point 但是值是Point2D的实例 比较Point2D实例的值和Point的结构后是相同的 因此没有报错
// 3. 属性相同,并且类型注解相同
// 2. 接口兼容性 // 参数多的可以兼容参数少的,即参数多的可以赋值给参数少的
interface Point {
x: number
y: number
}
interface Point2D {
x: number
y: number
}
interface Point3D {
x: number
y: number
z: number
}
let p: Point
let p2: Point2D = {
x: 1,
y:2
}
let p3: Point3D
p = p2
// 接口和类之间也是可以兼容的
class Point3D {
x:number
y:number
z:number
}
let p33 :Point2D = new Point3D()
// 3. 函数的兼容性 函数参数个数 参数类型 返回值类型
// 参数少的兼容参数多的 即参数少的可以赋值给参数多的--和接口兼容和类型联通都不一样 // 相同位置的参数类型要相同 // 返回值需要相同
type F1 = (a:number) => void
type F2 = (a:number,b:number) => void
let f1:F1 = (a)=>{
console.log(a);
}
let f2:F2 = f1
交叉类型 &
// 交叉类型 类似接口的继承功能 extends 用于组合多个类型为一个类型常用于对象类型
// 对比继承的举例子
interface Point2D {
x: number
y: number
}
interface Point3D extends Point2D {
z: number
}
let p3: Point3D = {
x: 1,
y: 2,
z: 3
}
// 交叉类型的举例
interface Person { "name": string }
interface Concat { "phone": string }
type PersonDetail = Person & Concat;
let p: PersonDetail = {
"name": "xx",
"phone": "sssss"
}
// 和继承的对比
// 都可以实现对象类型的组合 但是对于同名属性,处理类型冲突的方式不同
interface A {
fn: (value: number) => string
}
// interface B extends A {
// 此种会报错 应为类型不同
// fn:(value:string)=> string
// }
interface B1 {
fn: (value: string) => string
}
// 此种不会报错 最终的记过相当于 fn:(value: string|number)
type C = A & B1;
泛型
class也可以配合泛型类来使用
// 在保证安全的前提下,让函数等多种类型一起工作,从而实现复用,常用与接口,函数,class中
// 1. 创建一个函数实现传入的参数类型和返回的类型是一样的 传入参数可以是任意类型的值
// 创建泛型函数
function id<Type>(value: Type): Type {
// 语法 : 在 函数名称后面添加一个<>,尖括号中添加类型变量可以是任意名称 此处用Type
// 此处的Type 表示一种特殊的类型变量,他是处理类型而不是值
// 该类型变量相当于一个类型容器,捕获用户提供的类型
// 因为是类型 所以可以作为参数和函数的类型注解
// console.log(value.length); 提示type上不存在属性length
return value
// 实现了复用的同时也保证了类型的安全
}
// 数字类型的
const num = id<number>(1);
const str = id<string>("1");
// 2. 简化泛型调用机制
// 省略<Type> 根据类型推断机制自动判定
// 尽量还是不使用,id(100) 推断出来的类型是 100 而不是number
// 但是 num1 的类型是number
let num1 = id(100);
// 3. 泛型约束 // 默认情况下,泛型的类型变量type可以代表多个类型,会导致无法访问任何属性 // 比如id("a") 调用时就无法获取参数的长度 // 提示type上不存在属性length 此时就需要为泛型添加约束收缩类型
function id1<Type>(value: Type[]): Type[] {
// 1. 指定更加具体的类型
// 将数据类型改为 type 类型的数组 就会存在length 属性
console.log(value.length);
return value
}
interface Ilength { length: number }
function id2<Type extends Ilength>(value: Type): Type {
// 添加约束
// 1. 创建描述约束的接口 Ilength 提供length 属性
// 2. 通过 extends 关键字 使用该接口 为泛型添加约束
// 3. 约束表示 传入的类型必须具有 length 属性
console.log(value.length);
return value
}
// id2(["a", "b"])
// 4. 多个泛型的情况
// 创建一个对象获取对象中的属性的值
// 类型变量是可以出现多个的 并且变量之间也是可以约束的
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
console.log(obj[key]);
// keyof Type keyof 关键字接收一个对象类型 生成其间名的联合类型
// 此处获取的personx的结果 是 "name"|"age"
// 所以key 受到了 Type 的约束
return obj[key]
}
let personx = {
name: "jack",
age: 18,
}
getProp(personx, "name")
// 5. 泛型接口
// 接口后面添加 <类型变量> 接口就变成了泛型接口
// 接口的类型变量对内部所有成员可见
// 使用泛型接口是 需要显式的指定具体类型
// 如下例子 id 返回的类型是 number ids 返回的类型是number[]
interface IdFunc<Type> {
id: (value: Type) => Type
ids: () => Type[]
}
// number 是咸湿的指定泛型类型 因为接口没有类型推断的功能
let objx: IdFunc<number> = {
id(value) { return value },
ids() { return [1, 2, 3] }
}
// 在方法名上 ctrl + 点击 可以查看详情
// 6. 泛型类 // 在类名的后面加入<类型变量> 就可以表示为泛型类
class NumberAdd<NumType> {
defaultValue: NumType
add: (x: NumType, y: NumType) => NumType = (x, y) => {
console.log(x);
console.log(y);
return x
}
constructor(value: NumType) {
this.defaultValue = value
}
}
const myNum = new NumberAdd<number>(5)
myNum.defaultValue = 10
myNum.add(1, 2)
// 7. 高级类型 // 泛型工具类型
// 1. partial 用来创建一个类型 将type的所有属性设置为可选
//
interface Props {
id: string
children: number[]
}
type PartialDemo = Partial<Props>

// 2.readonly 构造一个类型 所有的属性都变为只读
interface Props1 {
id: string
children: number[]
}
type ReadonlyDemo = Readonly<Props1>
let props1: ReadonlyDemo = {
id: "1",
children: []
}
// props1.id = "2" 会报错
// 3.pick 从类型中选中一组属性来构造新的类型
interface Props2 {
id: string
children: number[]
}
type PickDemo = Pick<Props2,"id">
// 4. Record 构造一个对象类型 属性键为keys 属性类型为type
// 两个参数 一个表示对象有哪些属性 一个表示对象的属性的类型
type RecordObj = Record<"a"|'b'|'c',string[]>
// 上面的代码类似于下面的效果
type objR111 = {
a: string[],
b: string[],
c: string[],
}
// 使用
let objR:RecordObj = {
a: ['1'],
b: ['2'],
c: ['3'],
}
// 以上解释 对象有三个属性分别是 a b c 属性值的类型都是 string[]
索引签名类型
// 使用场景 当对象中无法确定都有哪些属性的时候 就没有办法提前定义 此时就可以使用索引签名
interface AnyObject{
[key:string]: number | string
}
let obj:AnyObject= {
a:1,
b:2,
c: "sss"
}
// 使用[key:string] 来约束出现的属性 key是占位符可以替换成任意的变量名称
// js对象中的键值因为都是string 类型的因此可以表示任意的属性
// 数组中的键值是number 类型的
映射类型
type propskey = 'x' | 'y' | 'z'
type propskey1 = {
a: number;
b: number;
c: boolean
}
// 原始写法
type list = {
x: number;
y: number;
z: number
}
// 使用联合类型的简化
type list1 = { [key in propskey]: number }
// 使用对象类型的简化 keyof propskey1 获取对象的key值的联合类型
type list2 = { [key in keyof propskey1]: number }
// 注意 映射类型只能在自定义类型中使用 不能使用在接口中
查询类型
type propskey = {
a: number;
b: number;
c: boolean
}
type a = propskey["a"]
// 查询多个
type b = propskey["a" | 'b']
type c = propskey[keyof propskey]
类型声明文件
类型声明文件 为已经存在的js库提供类型信息 然后使用这些库的时候他们也就有了类型保护机制了 xxx.d.ts就是类型声明文件
- 两种类型文件
.ts 包含类型信合和可执行的代码 可以被编译为js .d.ts 只包含类型信息 不会生成.js 文件 用于提供信息
- 类型问价的使用说明
definitelyType
比较全面的定义类型的库,基本包含所有的第三方库类型定义文件 ts 官方也提供一个类型定义查询 在tool 工具页面中 中文网站 www.typescriptlang.org/zh/tools