typescript学习(一)

255 阅读10分钟

本文章计划主要是学习typescript的基本知识总结,不涉及深入分析

typescript介绍

typescript是什么

TypeScript(简称:TS)是 JavaScript 的超集(JS 有的 TS 都有)。

TypeScript = Type + JavaScript(在 JS 基础之上,为 JS 添加了类型支持)。

TypeScript 是微软开发的开源编程语言,可以在任何运行 JavaScript 的地方运行。

typescript相对javascript优势

  1. 从编程语言的动静来区分,TypeScript 属于静态类型的编程语言,JS 属于动态类型的编程语言。

静态类型:编译期做类型检查; 动态类型:执行期做类型检查。 代码编译和代码执行的顺序:1 编译 2 执行。

  1. 程序中任何位置的代码都有代码提示,随时随地的安全感,增强了开发体验。

  2. 强大的类型系统提升了代码的可维护性,使得重构代码更加容易。

typescript初体验

typescript安装包

因为node.js不识别我们的ts,需要安装依赖进行转换 npm i -g typescript

验证是否安装成功:tsc –v(查看 typescript 的版本)

编译项目 tsc 文件名 (内部会把ts文件转换成为js文件)

执行 JS 代码:在终端中输入命令 node 转换的js文件

简化运行 TS 的步骤 问题:每次修改代码后,都要重复执行两个命令,才能运行 TS 代码,太繁琐。

简化:使用 ts-node 包,直接在 Node.js 中执行 TS 代码。

安装命令:npm i -g ts-node(ts-node 包提供了 ts-node 命令)。

使用方式:ts-node ts文件

解释:ts-node 命令在内部偷偷的将 TS -> JS,然后,再运行 JS 代码。

typescript常用类型

可以将 TS 中的常用基础类型细分为两类:1 JS 已有类型 2 TS 新增类型。

  • JS 已有类型

1.原始类型:number/string/boolean/null/undefined/symbol。 2.对象类型:object(包括,数组、对象、函数等对象)。

  • TS 新增类型

l 联合类型、自定义类型(类型别名)、接口、元组、字面量类型、枚举、void、any 等。

typescript原始类型

原始类型:number/string/boolean/null/undefined/symbol。

typescript对象类型

对象类型:object(包括,数组、对象、函数等对象)。

特点:对象类型,在 TS 中更加细化,每个具体的对象都有自己的类型语法。 一般使用interface声明具体属性类型,然后继承引用

interface Iprops {
    type:string
    children: Array<string>
}
let list :Iprops[]

数组类型

let list: string[] = ['1','2']
let newList: Array<string>  = ['1','2']
let newList: (string | number)[]   = ['1','2',3]

(竖线)在 TS 中叫做联合类型(由两个或多个其他类型组成的类型,表示可以是这些类型中的任意一种)。

函数类型 函数的类型实际上指的是:函数参数和返回值的类型。

为函数指定类型的两种方式: 1 单独指定参数、返回值的类型

function add (num1: number, num2:number) :number {
    return num1+num2 
}

2 同时指定参数、返回值的类型。

const add = (num1: number, num2:number) :number = number =>(num1: number, num2:number) {
    return num1+num2 
}

3 没有返回值

const add = (num1: number, num2:number) :void {
     num1+num2 
}

4.参数问题 (可传可不传)

const add = (num1?: number, num2?:number) :void {
     num1+num2 
}

注意:可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数。

typescript类型别名

类型别名(自定义类型):为任意类型起别名。

使用场景:当同一类型(复杂)被多次使用时,可以通过类型别名,简化该类型的使用。

type Iprops = number
let list :Iprops = 1

typescript类型接口

当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的。

interface Iprops {
    type:string
    children: Array<string>
}
let list :Iprops[]

type和interface的对比

  1. 两者都可以用在对象定义,但是type还可以用在其他类型定义,而interface只能用于对象型数据
interface Iprops {
    type:string
    children: Array<string>
}
let list :Iprops[]
type Iprops {
    type:string
    children: Array<string>
}
let list :Iprops[]
type a: string | number

 interface Dog { wang(); } 
 interface Cat { mao(); } 
 type P = Dog | Cat 
 // 具体定义数组每个位置的类型 
 type Pet = [Dog, P]
  1. 拓展的extend interface可以拓展extend,但是type不能
interface Name { name: string; }
interface User extends Name { age: number; }
type Name = { name: string; }
type User = Name & { age: number };
interface Name { name: string; } 
type User = Name & { age: number; }
  1. type 语句中还可以使用 typeof 获取实例的 类型进行赋值
// 当你想获取一个变量的类型时

typeof let div = document.createElement('div');
type B = typeof div
  1. interface 能够声明合并
interface User { name: string, age: number } 
interface User { sex: string }
/* User 接口为 { name: string age: number sex: string } */

  1. type 其他操作

typescript元组

确切地知道包含多少个元素,以及特定索引对应的类型 场景:在地图中,使用经纬度坐标来标记位置信息。

可以使用数组来记录坐标,那么,该数组中只有两个元素,并且这两个元素都是数值类型

let map: [number,number] =[123,12]

typescript类型推论

在 TS 中,某些没有明确指出类型的地方,TS 的类型推论机制会帮助提供类型。

换句话说:由于类型推论的存在,这些地方,类型注解可以省略不写!

发生类型推论的 2 种常见场景:1 声明变量并初始化时 2 决定函数返回值时

let name = 'kevin

function add(num1:number,num2: number) { return num1+num2 }

typescript类型断言

  • 使用as实现类型断言
let alink = ducument.getElementById('link') as HTMLAnchorElement
  • 使用 <> 语法
let alink = <HTMLAnchorElement>ducument.getElementById('link') 

typescript字面量类型

使用模式:字面量类型配合联合类型一起使用。

使用场景:用来表示一组明确的可选值列表。

function changeDirection (direction: 'up' | 'right' | 'down' | 'left') {}

typescript枚举

枚举的功能类似于字面量类型+联合类型组合的功能,也可以表示一组明确的可选值。

枚举:定义一组命名常量。它描述一个值,该值可以是这些命名常量中的一个。

enum direction {
     up,
     right,
     down, 
     left
}
function changeDirection (direction: direction) {}

直接通过点(.)语法访问枚举的成员

注意:枚举成员是有值的,默认为:从 0 开始自增的数值。

enum direction {
     up =10,
     right=11,
     down =12, 
     left =16
}

any 类型

原则:不推荐使用 any!这会让 TypeScript 变为 “AnyScript”(失去 TS 类型保护的优势)。

因为当值的类型为 any 时,可以对该值进行任意操作,并且不会有代码提示

除非临时使用 any 来“避免”书写很长、很复杂的类型!

其他隐式具有 any 类型的情况:1 声明变量不提供类型也不提供默认值 2 函数参数不加类型。

注意:因为不推荐使用 any,所以,这两种情况下都应该提供类型!

typescript高级类型

class 类

  • 类继承的两种方式:1 extends(继承父类) 2 implements(实现接口)。
class animal {
    move(){console.log('animal')}
}
class cat extend animal{
    eat(){console.log('cat')}
}

1.通过 extend 关键字让 class 实现接口。 2.子类 cat 继承父类 Animal,则 Dog 的实例对象 dog 就同时具有了父类 Animal 和 子类 Dog 的所有属性和方法。

class singable {
    sing()void
}
class person extend singable{
    sing(){console.log('sing')}
}

1.通过 implements 关键字让 class 实现接口。

2.Person 类实现接口 Singable 意味着,Person 类中必须提供 Singable 接口中指定的所有方法和属性。

  • 类成员可见性:可以使用 TS 来控制 class 的方法或属性对于 class 外的代码是否可见。

可见性修饰符包括:1 public(公有的) 2 protected(受保护的) 3 private(私有的)

static关键字, 用于定义类的数据成员(属性和方法)为静态的, 静态成员和方法可以直接通过类名调用。举例

class Dog {
    // 属性
    static color: string

    static show() {
        console.log(`我是一只${Dog.color}的狗`)
    }
}

Dog.color = "白色"
Dog.show();
  1. public:表示公有的、公开的,公有成员可以被任何地方访问,默认可见性。可以直接省略
class animal {
   public move(){console.log('animal')}
}
  1. protected:表示受保护的,仅对其声明所在类和子类中(非实例对象)可见。
class animal {
   protected move(){console.log('animal')}
}

class cat extend animal{
    eat(){
         this.move()
        console.log('cat')
    }
}
  1. private:表示私有的,只在当前类中可见,对实例对象以及子类也是不可见的。
class animal {
   private move(){
       this.move()
       console.log('animal')
     }
}

class cat extend animal{
    eat(){
         this.move() //不行
        console.log('cat')
    }
}

类型兼容性

两种类型系统:1 Structural Type System(结构化类型系统) 2 Nominal Type System(标明类型系统)。

TS 采用的是结构化类型系统,也叫做 duck typing(鸭子类型),类型检查关注的是值所具有的形状。

也就是说,在结构类型系统中,如果两个对象具有相同的形状,则认为它们属于同一类型。

class animal {
      name: string,
      age: string
}

class animalBig {
    name: string,
    age: string
}
const A: animal = new animalBig()

1.Point 和 Point2D 是两个名称不同的类。

2.变量 p 的类型被显示标注为 Point 类型,但是,它的值却是 Point2D 的实例,并且没有类型错误。

3.因为 TS 是结构化类型系统,只检查 Point 和 Point2D 的结构是否相同(相同,都具有 x 和 y 两个属性,属性类型也相同)。

4.但是,如果在 Nominal Type System 中(比如,C#、Java 等),它们是不同的类,类型无法兼容。

除了 class 之外,TS 中的其他类型也存在相互兼容的情况,包括:1 接口兼容性 2 函数兼容性 等。

l 接口之间的兼容性,类似于 class。并且,class 和 interface 之间也可以兼容。 函数之间兼容性比较复杂,需要考虑:1 参数个数 2 参数类型 3 返回值类型。

参数个数,参数多的兼容参数少的(或者说,参数少的可以赋值给多的)。

type addOne =(x:number,y:number)=> void
type addTow =(x:number,y:number,z:number)=> void
let f1: addOne
let f2:addtTwo = f1

参数类型,相同位置的参数类型要相同(原始类型)或兼容(对象类型)。

type addOne =(x:number,y:number)=> string
type addTow =(x:number,y:number,z:number)=> string
let f1: addOne
let f2:addtTwo = f1

返回值类型,只关注返回值类型本身即可 如果返回值类型是原始类型,此时两个类型要相同,比如,左侧类型 F5 和 F6。 如果返回值类型是对象类型,此时成员多的可以赋值给成员少的,比如,右侧类型 F7 和 F8。

交叉类型

交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)。

type addOne =(x:number,y:number)
type addTow =(z:number)
let f1 = addOne & addTow

使用交叉类型后,新的类型 PersonDetail 就同时具备了 Person 和 Contact 的所有属性类型

type addTow =(x:number,y:number,z:number)

交叉类型(&)和接口继承(extends)的对比: 相同点:都可以实现对象类型的组合。

不同点:两种方式实现类型组合时,对于同名属性之间,处理类型冲突的方式不同。

interface addOne {fn: (x:number,y:number) =>string}
interface addTow extend addOne{fn: (x:string,y:number) =>string}

interface addOne {fn: (x:number,y:number) =>string}
interface addTow extend addOne{fn: (x:string,y:number) =>string}
type c = addOne & addTow

泛型 和 keyof

泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而实现复用,常用于:函数、接口、class 中

function addOne<T> (x:T):T =>return x

1.语法:在函数名称的后面添加 <>(尖括号),尖括号中添加类型变量,比如此处的 Type。

2.类型变量 Type,是一种特殊类型的变量,它处理类型而不是值。

3.该类型变量相当于一个类型容器,能够捕获用户提供的类型(具体是什么类型由用户调用该函数时指定)。

4.因为 Type 是类型,因此可以将其作为函数参数和返回值的类型,表示参数和返回值具有相同的类型。

5.类型变量 Type,可以是任意合法的变量名称。

简化

let add = addOne<number>(10)

泛型约束:默认情况下,泛型函数的类型变量 Type 可以代表多个类型,这导致无法访问任何属性。

比如,id('a') 调用函数时获取参数的长度

function addOne<T> (x:T):T =>{
    console.log(x.length) //错误
    return x
}

1 指定更加具体的类型 改正

function addOne<T> (x:T[]):T[]=>{
    console.log(x.length) 
    return x
}

2 添加约束。 改正

interface Ilength {
    length: number
}
function addOne<T extend Ilength> (x:T[]):T[]=>{
    console.log(x.length) 
    return x
}

泛型的类型变量可以有多个,并且类型变量之间还可以约束


function addOne<T,y extend T> (x:T,y : y)=>{
    console.log(x.length) 
    return x.y
}

泛型接口:接口也可以配合泛型来使用,以增加其灵活性,增强其复用性

interface Ilength <T> {
    add :(value: T)=>T
}

泛型类:class 也可以配合泛型来使用。

比如,React 的 class 组件的基类 Component 就是泛型类,不同的组件有不同的 props 和 state

interface Istate { count: number}
interface Iprops{ maxvalue: number}
class InputCount extend React.Component<Istate,Iprops> {
    state:Istate ={
        count :1 
     }
     render(){
         return <div>{this.props.maxvalue}</div>
     }
}

创建泛型类:

class InputCount<numType> {
    defaultValue:numType,
    add: (x:numType,y:numType) =>numType
}

const myNum = new InputCount<number>()
myNum.InputCount = 10