• TS简单介绍以及用法


    TS简单介绍以及用法

    一、什么是TS

    TS是TypeScript的缩写,是JavaScript的超集 (JS有的TS都有),也可以说TS是 type + js,加了一个类型。比如:JS:let a = 0,TS:let a:number=0

    为什么需要给JS添加type呢?因为js的类型系统存在“先天缺陷”,JS代码中绝大部分的错误都是类型错误,那么就会存在一个问题,那就是找bug、改bug的时间增加,严重影响了开发效率。

    从编程语言的动静来区分;TS属于静态类型的编程语言,JS属于动态的编程语言。

    • 动态编程语言:执行期间做类型检查
    • 静态编程语言:编译期间做类型检查

    TS相对于JS的优势:

    • 更早发现代码中的错误,减少找bug、改bug的时间,提升了程序员开发效率。
    • 程序中任何位置的代码 都有 代码提示, 知道参数的类型是哪些,增加了开发体验。
    • 强大的 类型系统 提升了代码的可维护性,使得重构代码更加容易。
    • 支持最新的ECMAscript语法,体验最新的语法,让你走在前端开发的前沿。
    • TS类型推断机制,不需要你在代码中的每一个地方都添加类型标注,降低了开发成本。

    二、TS的常用类型

    • 原始类型:number、string、boolean、null、undefined、symbol
    • 数组类型:两种写法:1、number[] 2、Array< number > 定义两个:(number|string)[]
    • 对象类型:1、需要申明对象中的属性,例如:let obj :(name:string;age:number;sayWorld(a:string):void)= {name:'你好',age:22,sayWorld(a){}},
      2、可选参数和下面函数写法一致
    • 类型别名:type 如下:
    type stateArray = ( number | string )[]
    let a: stateArray =[1,2,'a']
    let b: stateArray =[3,4,'a',5]
    
    • 函数类型:1、单独制定参数、返回值类型 , 2、同时指定参数和返回值的类型
      特殊的函数类型 void (表示没有返回值)
      可选参数 在参数后面加一个问号 可选参数只能放在必选参数后面
    //1、单独制定参数、返回值类型
    function fn(num1:number,num2:number):number{
    	retuen num1+num2
    }
    const fun = (num1:number,num2:number):number=>{
    	retuen num1+num2
    }
    //2、同时指定参数和返回值的类型
    const fun1:(num1:number,num2:number)=>number = (num1,num2)=>{
    	reurn num1+num2
    }
    //可选参数 没有返回值
    const fun = (num1:number,num2?:number):void=>{
    }
    
    
    • 接口 interface关键词
    interface state {
    	name:string;
    	age:number;
    	sayHi():void
    	number?:number;
    }
    let obj:state  ={
    	name:'你好',
    	age:'22',
    	sayHi(){},
    }
    

    我们发现 type和interface的用法和写法非常像,其实两者是有区别的,
    interface只能为对象指定类型
    type不仅可以为对象指定类型,实际上可以为任何类型指定类型。

    • 接口 extends 关键字 例如:
    interface state2D{x: number; y: number}
    interface state3D extends state2D{z:number}
    let d3: state3D ={
    	x:1,
    	y:2,
    	z=3
    }
    
    • 元组类型

    元组类型是另一种类型的数组,他知道数组中到底有多少个元素,以及特定索引对应的类型let array:[number,number]=[1,2]

    • 类型推论

    在TS中,某些没有明确指出类型的地方,TS中的类型推论机制会帮助提供类型。也就是有些地方可以省略不写!
    发生类型推论的常见地方有两个:1、声明变量并初始化的时候 2、决定函数返回值
    在实际开发中,能省则省

    //1\声明变量并初始化的时候 
    let num = 10 //number类型
    let str = 'string'//string类型
    num = '10'//报错
     //2、决定函数返回值
     function add(num1:number,num:number){ //类型推断是 number
     return num1 + num2
     }
    

    到这里,又有人迷糊了,这不是和js是一样的吗?其实还是不一样的,在ts中 变量声明并且马上给初始值时。ts的类型推断会自动加上,在后面就不能赋值其他类型,否则会报错,js则不会

    • 类型断言 as

    有时候你会比TS更加明确一个值的类型,此时,可以使用 类型断言 来指定更具体的类型。比如:

    //html
    <a herf="http://baidu.com" id="linl">百度</a>
    //TS
     const aLink = document.getElementById('link) //aLink : HTMLElement
     //此时 aLink是HTMLElement 类型,只有一些基本的属性,访问不到 a标签的herf等属性
     // 此时我们需要用类型断言来解决这个问题
     const aLink = document.getElementById('link) as HTMLAnchorElement
     //使用 as 关键字实现类型断言
     //关键字 as 后面的类型是一个更加具体的类型 (HTMLAnchorElement 是 HTMLElement 的一个子类型)
     //通过类型断言,aLink的类型变得更加具体,这样可以访问a标签的特有属性和方法了
     
     //另一种写法
     const aLinl = <HTMLAnchorElement>document.getElementById('link') //react中用不了 冲突
    

    到这里就有人会问了,我怎么知道这些到底 具体是什么元素呢 这时候我们就可以利用浏览器打印来知道具体的类型,在审查元素选中标签, console.dir($0) 打印,saajbv点击打开 滑倒最下面可以看到了,英语比较好的 直接写就好了。

    • 字面量类型
      不同的声明方式,在TS中会有不同的数据类型,比如:
    let str1 = 'Hello TS' //String 数据类型
    const str2 = 'Hello TS' // Hello TS 数据类型
    '
    运行

    这是为什么呢?因为 let声明的是一个变量,可以更改值,所以类型为:string . 而const声明的是一个常量,不能更改值,值只能是Hello TS,所以TS默认他的数据类型是Hello TS

    使用场景:用来表示一组明确的可选值列表,比如需要声明一个函数,参数只能是 上下左右

    function fun (direction:'up'|'down'|'left'|'right'){
    }
    fun('up')
    //此时 fun函数只能接收 up down left right 四个参数 否则报错
    
    • 枚举类型

    枚举类型 就是字面量类型+联合类型组合的功能,也可以表示一组明确的可选值
    枚举:定义一组命名常量。他描述一个值,改值可以是这些命名常量中的一个。

    enmu Direction { Up,Down,Left,Right} //逗号分隔,不是| 枚举值以大写字母开头
    function fun (direction :Direction){
    }
    //形参direction 的类型为枚举Direction,那么实参的值就应该是Direction 中成员的任意一个。
    //枚举成员是有值的,是从0开始的自增数 Up:0 Down:1 Left:2 Right:3
    // 当然,也可以也可以给枚举中的成员初始化值。 enmu Direction { Up=2,Down=4,Left=6,Right=8}
    fun(Direction .Up)
    //如果enmu Direction { Up=2,Down,Left,Right} 那么 Down:3 以此类推
    //访问枚举成员直接使用点(.)语法。类似js中的对象.
    

    当然,枚举成员的值也可以是字符串,如果是字符串的话 那么枚举所有成员的初始值都需要被定义,因为字符串没有自增长行为。

    • any类型 (不推荐使用)

    这会让 'TypeScript’变成 ‘AnyScript’ (失去TS的类型保护的优势),因为当值的类型是any时,可以对该值进行任意操作,并且不会有代码提示,
    具有隐式 any 类型的情况:声明变量不提供类型也不提供默认值,函数参数不加类型

    • typeof 操作符

    众所周知,在js中 typeOf 是用来获取数据类型的,实际上,TS也提供了 typeOf 操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。当然在TS中也可以使用JS中的用法。两者区别如下:

    let p = {
    	a: 1,
    	b: 2,
    }
    //js
    console.log(typeof p) //打印 'object'
    //TS
    function fun(point:{a:number,b:number}){}
    fun(p)
    //可以用typeof 进行优化
    function fun (point: typeof p){}
    fun(p)
    //当然 也可也获取对象中的类型
    let c:typeof p.a //c的类型就是number
    

    三、TS的高级类型

    相对于JS中class类,在TS中添加了几种方法: implements(实现接口)、可见性修饰符:pubilc(公有的),protected(受保护的),private(私有的),readonly(只读)。

    1、JS里的使用
    //JS中的基本使用
    class Person{
    	name: string //没有默认值的 需要添加类型 如果没有添加 会默认为 any 
    	//age = 18 //如果有默认值,不需要添加类型,有类型推断 会自动默认为number
    	// age:number = 18  //这样写也是可以的 不过我们能省则省
    	age :number
        //构造函数
    	constructor(name:string,age:number){
    		this.name = name
    		this.age = age
    	}
        //实例方法
        AddAge(n:number){
            this.age +=n
        }
    }
    const p = new Person('张三',18) //实例对象 P 的类型就是 Person
    p.AddAge(2)
    console.log(p)// Person { name: '张三', age: 20 } 用 . 可以访  具体属性'
    
    // 继承 extends 
    class Son extends Person{
        rideAge(n:number){
            this.age *=n
        }
    }
    const s = new Son('李四',18)
    console.log(s.name) //李四
    console.log(s.age) //18
    s.AddAge(1)
    console.log(s.age) //19
    s.rideAge(2)
    console.log(s.age) //38
    
    2、 implements(实现接口)
    // implements
    interface state{
        sing():void
    }
    class Person implements state{
        sing(){
            console.log('唱歌')
        }
    }
    
    3、可见性修饰符
    (1)、public(公开的)
    // 默认就是 public(公开的) 写不写都可以
    class Example {
        public move(){
            console.log('移动了')
        }
    }
    
    (2)、protected(受保护的)
    //protected 受保护的 只能在class本身 或者子类中使用
    class Person {
        protected move(){
            console.log('移动了')
        }
    }
    class Son extends Person{
        run(){
            this.move() // 正常  可以调用到
        }
    }
    const p =new Person()
    p.move // 报错 属性“move”受保护,只能在类“Person”及其子类中访问
    const s =new Son()
    s.move // 报错 属性“move”受保护,只能在类“Person”及其子类中访问
    
    
    (3)、private(私有的)
    //private(私有的) 只能在当前class内部使用
    class Person1 {
        private move(){
            console.log('移动了')
        }
    }
    class Son1 extends Person1{
        run(){
            this.move() // 报错 属性“move”为私有属性,只能在类“Person1”中访问
        }
    }
    const p1 =new Person1()
    p1.move // 报错 属性“move”为私有属性,只能在类“Person1”中访问
    const s1 =new Son1()
    s1.move // 报错 属性“move”为私有属性,只能在类“Person1”中访问
    
    (4)、readonly(只读属性)
    //readonly 只读属性
    class Person2{
         //如果属性设置为 readonly 需要指定类型 
        //  不指定类型又有初始值时 类型会默认为初始值 
        // 不指定类型又没有初始值时,类型为any
        readonly age:number = 18
        constructor(age:number){
            this.age = age
        }
        // 错误演示 readonly不能对方法使用
        // readonly say(){} //报错"readonly" 修饰符仅可出现在属性声明或索引签名中。
    }
    // readonly 不仅仅可以使用在class类里面 还可以在接口interface 和{}类型声明 里面使用
    interface state {
        readonly name:string
    }
    let state:state = {
        name:'1'
    }
    state.name = '2' // 报错 无法分配到 "name" ,因为它是只读属性。
    
    let obj:{ readonly name:string} = {
        name:'1'
    }
    obj.name = '2' // 报错 无法分配到 "name" ,因为它是只读属性。
    
    • 类型兼容性

    两种类型系统 1、Structural Type System(结构化类型系统) 2、Nominal Type System(标明类型系统)
    TS采用的是 结构化类型系统,类型检查关注的值所具有的形状
    在TS中,主要是 对象、接口、函数的类型兼容性。

    • 交叉类型 (&

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

    // 交叉类型
    interface Person{
        name:string
    }
    interface Son{
        age:number
    }
    type SonD = Person & Son
    let S :SonD ={
        name:'张三',
        age:18}
    let S1:(Person & Son)={
        name:'张三',
        age:18}
    

    在这里可以看出 交叉类型和 继承好像没有什么不同,那我们来说一下两者不同的地方,如果属性或者方法又冲突的时候,两者处理的方式不一样,看下面代码

    interface Person{
        say:(a:number)=>string
    }
    // 继承
    interface Son extends Person{
        say:(a:string)=>string
    }
    // 报错
    // 接口“Son”错误扩展接口“Person”。
    //   属性“say”的类型不兼容。
    //   不能将类型“(a: string) => string”分配给类型“(a: number) => number”。
    //     参数“a”和“a” 的类型不兼容。
    //       不能将类型“number”分配给类型“string”。
    
    //交叉类型 
    interface Son1{
        say:(a:string)=>string
    }
    type SonD = Person & Son1
    let S1:SonD ={
        say(value:number|string){
            return ''
        }
        // (property) say: ((a: number) => string) & ((a: string) => string)
    } 
    
    
    • 泛型和keyof

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

    1、泛型基本使用
    function fun<Type>(val:Type):Type{
        return val
    }
    fun<number>(1) //function fun(a: number): number
    fun<string>('1') //function fun(val: string): string
    // 简化
    fun(1) //function fun<1>(val: 1): 1
    
    2、泛型约束

    主要有两种方式:1、指定更加具体的类型 2、添加约束
    如果不添加类型约束,那么参数可以是任何的类型,所以没有办法调用任何的方法 所以需要添加类型约束。

    两种方式代码如下:

    // 1、指定更加具体的类型 比如指定数组,我们就可以访问数组中的.length属性
    function fun <Type>(val:Type[]):Type[]{
        console.log(val.length)
        return val
    }
    fun<string>(["1","2"])
    // 2、添加约束 
    interface ILength{
        length:number
    }
    // 只要含有length属性都可以
    function fun1<Type extends ILength>(val:Type):Type{
        console.log(val.length)
        return val
    }
    fun1([1,2])
    fun1('123456')
    fun1({length:50,age:18})
    fun1(123) // 报错 类型“number”的参数不能赋给类型“ILength”的参数
    
    // 泛型变量可以是多个 看下面代码 
    // keyof 关键字 接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
    function fun2<Type ,Key extends keyof Type>(obj:Type,key:Key){
        return obj[key]
    }
    // 一般情况
    let person = {name:'张三',age:18}
    fun2(person,'name')
    // 了解补充
    fun2('asd','charAt')
    fun2(12,'toFixed')
    fun2('asd',1)
    

    泛型接口和泛型类

    // 泛型接口
    interface stateId<Type>{
        id:(val:Type)=>Type
        ids:()=>Type[]
    }
    let obj:stateId<number>={
        id(val){
            return val
        },
        ids(){
            return [1,2,3]
        }
    }
    // 其实 数组就是一个泛型接口 
    let arr = [1,2,3]
    arr.forEach(item=>{ //(method) Array.forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void
    })
    
    // 泛型类
    class Person<Type>{
        defaultValue:Type
        add:(num1:Type,num2:number)=>number
    }
    let p = new Person<number>()
    p.defaultValue = 10
    //可以省略不写
    class Person1<Type>{
        defaultValue:Type
        constructor(value:Type){
            this.defaultValue = value
        }
    }
    let p1 = new Person1(10)
    

    泛型工具类型

    在TS中,内置了一些泛型工具类型,来简化TS中的一些常见操作主要知道4个就行:
    Partial< Type > 、 Readonly< Type >、 Pick< Type,Key >、Record < Keys,Type >

    // Partial< Type > 把已有的全部转换成可选属性
    interface state {
        id:number,
        ids:number[]
    }
    type PartialState =  Partial<state>
    // 变换成这样 全部属性变成可选
    // type PartialState = {
    //     id?: number | undefined;
    //     ids?: number[] | undefined;
    // }
    let P:PartialState = {}
    
    // Readonly< Type >
    interface state1 {
        id:number,
        ids:number[]
    }
    type ReadonlyState1 =  Readonly<state1>
    // 变换成这样 全部属性变成可选
    // type PartialState = {
    //     id?: number | undefined;
    //     ids?: number[] | undefined;
    // }
    let P1:ReadonlyState1 = {
        id:1,
        ids:[1]
    }
    P1.id = 10 //报错 无法分配到 "id" ,因为它是只读属性。
    
    // Pick< Type,Key > 选择一些属性来构造新的类型
    interface state2 {
        id:number,
        ids:number[]
    }
    type PickState2 =  Pick<state2,'id'>
    // 变换成这样 全部属性变成可选
    // type PartialState = {
    //     id?: number | undefined;
    //     ids?: number[] | undefined;
    // }
    let P2:PickState2 = {
        id:1,
        ids:[1] // 报错 不能将类型“{ id: number; ids: number[]; }”分配给类型“PickState2”。对象文字可以只指定已知属性,并且“ids”不在类型“PickState2”中。
    }
    
    //Record  构建一个对象类型,属性的键为Keys,属性类型为 Type
    type RecordObj = Record<'a'|'b'|1,string[]>
    let obj:RecordObj = {
        a:['1'],
        b:['1'],
        1:['1']
    }
    
    • 索引签名类型和索引查询类型

    在开发中,当无法确定对象中有哪些属性的时候,就要用到索引签名类型了。

    // 对象 对象中的键值一定是字符串类型
    interface state {
        [key:string]:string
    }
    let obj:state={
        a:'1',
        b:'2',
    }
    // 在TS中 数组是一个特殊的对象 特殊在数组的键(索引)是数值类型
    interface MyArray<T>{
        [n:number]:T
    }
    let arr:MyArray<number> = [1,2,3]  
    
    • 映射类型

    映射类型:基于旧类型创建新类型,减少重复书写、提升开发效率。
    映射类型 没办法在interface 接口中使用

    type propsKey = 'X'|'Y'|'Z'
    // type1 type2 实现效果一样
    type Type1 = {x:number,Y:number,z:number}
    type Type2 = {[key in propsKey]:number}
    // type Type2 = {
    //     X: number;
    //     Y: number;
    //     Z: number;
    // }
    // 基于对象 keyof 获取键值 相当于 'X'|'Y'|'Z'
    type propsKey2 = {x:number,Y:string,z:boolean}
    type Type3 = {[key in keyof Type1]:number}
    // type Type3 = {
    //     x: number;
    //     Y: number;
    //     z: number;
    // }
    

    索引查询(访问)类型

    type a = {x:number,b:string,c:boolean}
    type b = a['x'] //type b = number
    //模拟 Partial 实现
    
    type MyPartial<T>{
        [p in keyof T]?:T[p]
    }
    type PartialStates = MyPartial<a>
    // type PartialStates = {
    //     x?: number | undefined;
    //     b?: string | undefined;
    //     c?: boolean | undefined;
    // }
    
    // ---
    // 同时查询多个索引的类型
    type c = a['b'|'c'] //type c = string | boolean
    type d = a[keyof a] //type d = string | number | boolean
    

    感谢浏览观看 关注博主 一起学习!!!

  • 相关阅读:
    JavaScript 验证 API
    鲁大师电动车智能化测评报告第二十三期:实测续航95km,九号Q90兼顾个性与实用
    PagerDuty使印象笔记快速响应问题
    论文开题报告的研究基础怎么写?
    JavaScriptJQuery_jQuery选择器
    matlab 坡度滤波算法地面分割
    Linux内核—模块编译方法
    卓豪再签洛钼集团,实现AD域自动化管理有效降低管理人员工作负荷
    八股文第十七天
    “蔚来杯“2022牛客暑期多校训练营6 A题: Array
  • 原文地址:https://blog.csdn.net/tinglis/article/details/127055562