Typescript基础篇

178 阅读10分钟

TypeScript介绍

    TypeScript: 静态类型检查器
    TypeScriptJS的超集   为强类型    JavaScript为弱类型
    Ts === Js +  类型      js有的ts都有
    TS 规范了 js类型声明 使它更严谨

快速上手

  • 依赖安装
    npm  i -g  typescript  // 全局安装Typescript
  • 运行
    tsc  ts文件            每次都需要执行
    tsc  ts文件  --watch   保存就会运行,动态监听

类型注解

    let age:number = 18     //:number   表示类型注解
    let name:string = "hello"  
    // ---类型注解约束了只能给该变量赋值该类型的值

基础类型

原始类型
    // 原始类型   意思就是JavaScript 已有类型
    // 原始类型: number/string/boolean/null/undefined/symbol
    // 对象类型: object(数组、对象、函数等)
    let username:string = "hello"
    let age:number = 18
    let sex:boolean = true
    let job:null = null
    let salary:undefined = undefined

    //  2.
    // 联合类型、自定义类型(类型别名)、接口、元祖、字面量类型、枚举、void、any 等
    export {}   // 不加为全局模块   加了就是局部模块

对象类型

数组类型
    // 命名数组
    // 1. 类型[]  写法  
    let userList:string[] = ["一号","二号"]
    userList.forEach(item=>{
        console.log(item)
    })
    let numList:number[] = [1,2,3,436,5,7]
    numList.forEach(item=> {
        console.log(item)
    })

    // 2. Array<类型>  泛型
    let userList1:Array<string> =  ["一号","二号"]
    let numList1:Array<number> =  [1,2,3,436,5,7]
函数类型
    // 必须要指定函数得参数类型
    function add1(num1:number, num2:number):number {
        return num1+num2    // 必须返回number类型
    }
    add1(1, 2)

    let sum:number = add(1,2);
    console.log(sum, '----');


    // 箭头函数声明类型
    const add2 = (num1:number, num2:number):number =>{
        return num1+num2
    }
    let sum1:number = add2(1,4);
    console.log(sum1, '----');


    // 同时指定参数和返回值
    // 自定义类型的函数用的返回值 指定 不再是:   而是箭头函数 =>     
    // 自定义了一个函数类型 指定了函数类型的参数  返回值
    // 一旦用上了type关键字  此时我们代码处于类型上下文  ===  遵循ts语法规范及运行守则
    type CustomFunc = (num1:number, num2:number) => number

    // 类型兼容性(暂且放下)
    const add3: CustomFunc = () => {
        return 123
    }
    let sum2:number = add3(1,4);
    console.log(sum2, '----');

    // 假如函数没有返回值  可以使用void关键字来指定
    type CustomFunc2 = () => void
    // 表示函数没有返回值
    const add4: CustomFunc2 = () => {
        // 类型的兼容性
        return 1213
    }
    // 不写表示add5返回 void类型
    const add5 = () => {}

    // 定义void类型的函数  add5 === add6
    const add6 = ():void => {}

    // 如果返回类型为undefined  则必须去返回一个undefined
    const add7 = ():undefined => undefined
对象类型
    export {}
    // 对象类型
    let person:{
        username: string,  // 用户名为string类型
        age: number,  
        run: Function    // 属性名:Function
    } = {
        username: 'hello',
        age: 18,
        run:function () {
            console.log('hello加油')
        }
    } 
    person.run()

    // 对象中属性为函数时 ts的写法
    let person2: {
        run():string  // 方法():返回值
    } = {
        run(){
            return "hello"
        }
    }
    // 方法名: Function  方法名(): string
    // 方法名: () => string
    let person3: {
        run: () => string  // 方法():返回值
    } = {
        run(){
            return "hello"
        }
    }

    // 使用自定义类型来定义对象类型
    type CustomObj = {
        userName: string
        age: number
        run: () => string   // 可以指定返回值
        // run():string     // 也可以
        // run: Function        // 无法指定     
    }

    // 使用定义好的CustomObj类型
    let person4: CustomObj = {
        userName: 'l',
        age: 1,
        run() {
            return 'll'
        }
    }

TypeScript 新增类型

联合类型 |
    //当有多个类型的时候  可以用  |(竖线)   分割多个类型 
    let salary: number | string | null | undefined | boolean = "25k"
    salary = 100
    salary = null
    salary = undefined
    salary = true

    // 如果数组中存在多个 字符串或者是数组
    // 1,
    let arr1:string[] | number[] = [3, 213, 221, 44, 21]
    arr1 = ["1", "2"]

    // 2,
    let arr2:Array<string | number> = ["1", 2, "2"]

    // 3,
    let arr3:(string | number)[] = [1, 2, "2"]
交叉类型 &
    
自定义类型(类型别名)
    // 一个类型被频繁使用
    let arr1:(string | number)[] = [1, 2, "2"]
    let arr2:(string | number)[] = [1, 2, "2"]
    let arr3:(string | number)[] = [1, 2, "2"]

    // type  自定义类型 = 具体类型  可以使用自定义类型代指  具体类型
    type CustomArr = Array<string | number>   //声明一个自定义类型

    let arr4: CustomArr = [1,2,2]
    // type  自定义类型 = 具体类型 
接口 interface
    export {}
    /**
     * 接口类型 interface 来声明接口 
     * 声明之后  直接使用接口称作为变量的类型
     * */ 
    interface IPeolple {
        useranme: string
        age: number
        run: () => void
    }

    let num1: IPeolple = {
        useranme: '132',
        age: 1,
        run: () => {
            // void  允许为空
        }
    }
    /**
     * 接口 与  自定义类型的区别
     * 相同: 都可以给对象指定类型 
     * 不同: 接口只能为对象指定类型  类型别名可以为任意类型指定别名
     * 推荐  type
     */  
    /**
     * 接口继承
     */
    interface test1 {
        x: number
        y: number
    }
    interface test2 {
         x: number
         y: number
         z: number
     }
    interface test2 extends test1 {
        z: number
    }
    let test: test2 = {
        x: 1,
        y: 2,
        z: 3
    }
元祖 turple
    export {}
    /**
     * 元祖类型
     * 定义一个数组中具体索引位置的类型时  可以使用元祖
     */

    // 正确示范
    let position: [number, number] = [1, 3]  

    // 错误示范   //第二个必须为number类型
    // let position1: [string, number] = ['string', "1132"]  
字面量类型
    /**
     * 字面量类型
     * 字面量其实就是一个固定得值  可以跟联合类型搭配使用
     */

    // 推论出类型为string
    let user = 'll'

    // 推论出类型为124
    /**
     * 原因:  因为str是常量  不能变换  只能是124
     *  124 就是一个字面量类型  即特殊的字符也可以作为ts中的类型
     */
    const str = '124'

    /**
     * 使用场景:
     * 1,字面量与联合类型一起使用
     */

    type dess = 'up' | 'down' | 'left' | 'right'

    function test (qwe:dess) {

    }
    test('down')
    let str1:1 = 1
枚举 enum
    export {}
    /**
     * 枚举类型
     */
    // 枚举会有一个自增的值从0开始  
    // 需要指定  可以修改第一个值开始递增
    // left=0  right=1 up=2 down=3  依次递增
    enum lis {
        left = 1,
        right,
        up,
        down,
    }
    function test (l:lis) {}
    test(0)
    test(1)
    // ===
    test(lis.left)
    test(lis.right)

    enum lis1 {
        left = 'left',
        right = 'right',
        up = 'up',
        down = 'down',
    }
    function test1 (wq:lis1) {}
    test1(lis1.down)
    test1(lis1.left)
对象的可选属性
    // 使用自定义类型来定义对象类型
    //  ? 表示可选属性  可选可不选
    type CustomObj = {
        userName: string
        age?: number   // ? 将属性变为可选属性
        run?: () => string
    }

    // 使用定义好的CustomObj类型
    let person4: CustomObj = {
        userName: 'l',
    }
any 任意类型
    /**
     * any类型
     */
    let str:any = ''
    str = 1
    str = true
    str = Object
    str = null
    str = undefined
    str = []
    str = {}

    /**
     * 当ts有问题时候  可以作为一个临时的解决方案
     * 可以解决任意问题
     */

     // 推荐  尽量少用any类型, 用any不如去写js
可选参数
    // ? 可选链 表示参数可传不传
    type CustomFunc = (start?:number, end?:number) => void
        // 可选参数的位置必须处于必选参数的后面
        const slices: CustomFunc = () => {
    }
    slices()
    slices(1)
    slices(1,2)
类型推断(推论)
    /**
     * 类型推断
     * ts帮助我们自动推导出类型
     */

    /**
     * 出现场景
     * 1. 声明变量初始化时候
     * 2. 决定函数返回值时候
     */

    let useranme = 'hello'  // 已经被推断为string类型
    // useranme = 214  // 报错
    useranme = '214'   // 正确

    // 自动推导出返回值为number类型
    const add = (num1: number, num2: number) => {
        return num1+num2
    }
    add(1, 2)

    function add1(num1: number, num2: number) {
        return num1+num2
    }
    add1(1,2)

    /**
     * 推荐能够省略类型注解的地方就省略  充分利用ts提升开发效率
     */

TS类型,兼容性及常用类型

ts类型断言
    /**
     * 类型断言
     */
    // TS只能知道给你的类型是HTMLElement 
    // HTMLAnchorElement  为  HTMLElement 的子类型
    // 使用  as  实现类型断言   强制转换为我们想要的类型
    const link = document.getElementById('link') as HTMLAnchorElement

    // 类型断言其实可以理解为强制转换
    link.href = "https://www.baidu.com"

    type test1 = {
        user: string
        age: number
    }

    type test2 = {
        user: string
        age: number
        sex: string
    }

    let res1: test1 = {
        user: 'name',
        age: 19
    }

    res1 = {
        user: 'name',
        age: 19,
        sex: 'man'
    } as test2    //  类型断言可以转换类型   但是不能改变初始对象
    /**
     * 场景 :
     * 当我们的类型断言只能 推导出一个宽泛类型的时候  可以用类型断言直接推导
     */
ts中的typeof
    /**
     * typeof
     * JS:  获取数据的类型   typeof "hello"
     */
    // JS
    console.log(typeof "hello")   //返回hello的类型 为string

    // TS
    let test = {
        a: 1,
        b: 2
    }
    type res = {
        a: number
        b: number
    }
    function sum (avg:res) {}
    sum(test)

    // 这时候 typeof 的上下文处于ts环境上下文
    // 处于冒号后面的 处于ts环境上下文
    type testCum =  typeof test
    function sum1 (p: testCum) {} 
    sum1(test)
对象类型的兼容性
    /**
     * 对象类型的兼容性
     */
    type test1 = {
        name: string,
        age: number,
        sex: string,
        address: string
        salary: number
    }
    
    type test2 = {
        name: string,
        age: number,
        sex: string
    }
    
    let res1:test1 = {
        name: 'hello',
        age: 18,
        sex: 'nan',
        address: '西安',
        salary: 12
    }
    
    let res2: test2 = res1
    /**
     * ts是结构化的类型系统
     * 只有结构相同的类型 才能相互赋值
     */
     
    /**
     * 对象兼容性
     * 对象的多的属性可以赋值给对象少的属性 
     */
函数类型的兼容性
    /**
     * 函数类型的兼容性
     * 需要考虑:
     *   参数的个数   
     *   返回值类型
     */
    const arr = ['a', 'b', 'c']
    arr.forEach(()=>{})
    arr.forEach((item)=>{})
    arr.forEach((item, index)=>{})
    arr.forEach((item, index, arr)=>{})

    // void 表示没有返回值
    type test1 = (x: number) => void
    type test2 = (x: number, y: number) => void

    /**
     * 函数类型的兼容性
     *      参数类型少的  可以  赋值给参数多的
     * 返回值
     *      void  可以返回其他值
     *      要求其他值?  必须返回
     */
    let fun1: test1 = (a: number) => {
        return a   // 即使返回也不会造成影响
    }
    let fun2: test2 = fun1
泛型及用法
    /**
     * 什么是泛型? 
     *      让函数等 与  多种类型一起工作  实现复用
     *      常用于 函数  接口  class中
     */
    function test1 (value: string): string {
        return value
    }
    function test2 (value: number): number {
        return value
    }
    function test3 (value: boolean): boolean {
        return value
    }

    // 这里的 T  就是泛型的变量  T 可以是任意类型 number  / { } /
    // 保证类型的安全 不丢失类型信息的同时 函数可以和多种不同的类型一起工作 灵活使用
    /**
     * @param T 
     * @returns   T他=它处理的是类型而不是值
     * 
     * 能够捕获用户提供的类型  具体的类型有用户调用该函数时<T>指定
     */
    function com<T>(value: T): T {
        return value
    }
    com<string>('1')   //类型推论
    com(1) 

    /**
     * 泛型函数语法  保证类型安全  不是any
     */
    type test = <T>(value1: T, value2: T, value3: T) => T
    let fun: test = (value) => {
        return value
    }
    // let str:string = fun<number>(1,3,3) 
    fun<number>(1,3,3)
泛型约束
    /**
     * 泛型约束
     * 默认情况下 Type可以代表任意类型  导致无法访问任何属性
     */

    // 1,指定更加具体的类型  给T添加[]
    type test = <T>(value: T[]) => T[]

    let fun: test = (value) => {
        // 这里T泛型无法找到属性
        console.log(value.length);
        return value
    }
    fun(['1','2'])

    /**
     * 自定义类型;
     * 接口中必须有 length
     */
    interface ILength {
        length: number
    }
    let x1:ILength = "hello"
    let x2:ILength = [1,31,313]

    // 使用泛型约束传入的类型  必须有length属性
    type cus= <T extends ILength> (value: T) => T
    // T extends ILength 约束了 传入的泛型必须拥有length属性
    let fun1: cus = (value) => {
        return value
    }
    fun1("321321")
多个类型变量之间的约束
/**
 * 多个类型变量之间的约束
 */
// 定义一个方法 获取对象中属性的值
const obj = {name: "hello", age: 16, height: '180cm'}

function getKey (obj: {}, key: string) {}

getKey(obj, "wqe")  //有错
/**
 * 定义泛型的语法 
 * extends  继承
 * keyof    表示继承对象中键中的一个值 ===  键值
 */
// 传入泛型之后 要约束第二个参数 必须为T参数的key的值
// Key extends keyof T (Key是自定义变量名的类型名称)
function getKey1<T extends object, Key extends keyof T>(obj:T, key: Key | ""){   
    // 这时候第二个参数 受第一个参数的约束 必须是第一个参数中 key中的一个值
}
getKey1(obj, 'age')
// getKey1<>()
泛型接口
    /**
     * 泛型接口
     */
    interface Fun1 {
        fun: (val: string) => string
        fun1: () => string[]
    }

    interface Fun2 {
        fun: (val: number) => number
        fun1: () => number[]
    }

    interface Fun3 {
        fun: (val: object) => object
        fun1: () => object[]
    }

    // 使用泛型来解决  复用的问题  
    interface Test <T>{
        fun: (val: T) => T   //  ? 表示可选
        fun1?: () => T[]
    }
    let obj1: Test<number> = {fun(num){return num}};  obj1.fun(111);
    let obj2: Test<object> = {fun(obj){return obj}};  obj2.fun({a:1});
    let obj3: Test<string> = {fun(str){return str}};  obj3.fun('来了');
泛型工具 Partial
    type test = {
        name: string
        height: number
        money: number
        address: string
        is_marry: boolean
        cmpany: string
        wechat: string
    }

    let res: test = {
        name: 'hello',
        height: 180,
        money: 10000,
        address: '北京',
        is_marry: true,
        cmpany: "ll",
        wechat: "hkj"
    }

    /**
     * Partial 可以将传入的属性都变为可选的
     */
    type test1 = Partial<test>
    let res1:test1 = {}
    res1.cmpany = "dfasf"

    /**
     * 源代码Partial
     */
     type Partial<T> = {
         [P in keyof T]?: T[P];
     }
ReadOnly 只读类型
type test = {
    name: string
    height: number
    money: number
}
/**
 * ReadOnly  将类型中的所有属性变为只读属性
 */
type res1 = Readonly<test>

let qw: test = {
    name: 'hello',
    height: 180,
    money: 10000,
}
qw.money =  3214

let qw1: res1 = {
    name: 'hello',
    height: 180,
    money: 10000,
}
qw1.name = "adsg"   // 只能读取  不能更改
Pick类型 构造新类型
interface Props {
    id: number,
    title: string,
    children: number[]    
}

/**
 * Pick 选择从属性中构造新类型
 */
type PickProps = Pick<Props, 'id'>;  

let test:PickProps = {
    id: 1,
    title: '214'  // 对象文字可以只指定已知属性,并且“title”不在类型“PickProps”中
}