// 建议你每行代码都都敲一遍

TypeScript 中最基本的数据类型包括布尔、数字、字符串、null、undefined

let courseName:string = "学习"
let price:number = 129
// price = '12'
let isOnline:boolean = true
let courseSales:undefined
let timer:null = null
let me:[string,number] =['张三',12]
// me[0]= 1

当你不确定某个变量是什么类型时,你可以使用 any 作为这个变量的类型

let anThing
let anyCourse:any =1
anyCourse='aaa'
// console.log(anyCourse)

使用 enum 去定义枚举类型,这样可以把类型限制在指定的场景之内。

enum 课程评分 {好,非常好,嘎嘎好}
// console.log(课程评分['好']===0)
let scores = [课程评分['好'],课程评分['非常好'],课程评分['嘎嘎好']]
// console.log(scores)
let course1:string|number ="111"
course1=123
// course1=false

通过这些基础类型,通过组合的方式组合出新的类型,最常见的组合方式就是使用 | 实现类型联合。

type courseScore = 'good'|'very good'|'gaga good'

// let score1:courseScore ='111'
let score1:courseScore ='good'

通过 interface 接口可以定义对象的类型限制

interface geekTime{
    name:string,
    price:number,
    user:string,
    pic?:string|boolean,//?设置为可选属性
    readonly address:string
}

let vueCourse:geekTime={
    name:"vue",
    price:123,
    user:"students",
    address:"time.geekTime.org"
}
vueCourse.pic = false
// vueCourse.address = "1222"

然后我们学一下函数的类型限制。其实函数的定义,参数和返回值本质上也是变量的概念,都可以进行类型的定义。

function add(a:number,b:number):number{
    return a+b
}
let add1 = add(4,6)
// console.log(add1)

我们也可以使用变量的方式去定义函数,直接使用 (参数类型) => 返回值类型的语法去定义 add1 的变量类型,但是这样写出来的代码可读性稍差一些,我更建议你使用 type 或者 interface 关键字去定义函数的类型。

let add2:(a:number,b:number)=>number = function(x:number,y:number):number{
    return x+y
}
type addType = (a:number,b:number)=>number
let add3:addType = function (x:number,y:number):number{
    return x+y
}

interface addType1{
    (a:number,b:number):number
}
let add4 :addType1 = function (x:number,y:number):number{
    return x+y
}

如果你的函数本来就支持多个类型的参数,下面的代码中 reverse 函数既支持数字也支持字符串。我们的要求是如果参数是数字,返回值也要是数字,参数是字符串返回值也只能是字符串,所以参数和返回值都用 number|string 就没法精确地限制这个需求。我们需要使用函数重载的方式,定义多个函数的输入值和返回值类型,更精确地限制函数的类型。

function reverse(x:number):number
function reverse(x:string):string
function reverse(x:number|string):number|string|void{
    if(typeof x ==="number"){
        return Number(x.toString().split('').reverse().join(''))
    }else if( typeof x==="string"){
        return x.split('').reverse().join('')
    }
}
// console.log(reverse("123"))
// console.log(reverse(456))

日常开发中有很多浏览器上的变量和属性,这些怎么限制类型呢?关于宿主环境里的类型,TypeScript 全部都给我们提供了,我们可以直接在代码中书写:Window 是 window 的类型,HTMLElement 是 dom 元素类型,NodeList 是节点列表类型,MouseEvent 是鼠标点击事件的类型……

let w:Window = window
// w.alert(1)
let ele:HTMLElement = document.createElement('div')
let allDiv:NodeList = document.querySelectorAll('div')

ele.addEventListener("click",function(e:MouseEvent){
    const args:IArguments = arguments
    w.alert(1)
    console.log(args)
},false)

泛型

把所有变量和函数出现的地方都定义好类型,就可以在编译阶段提前规避出很多报错。然而 TypeScript 的能力可不止于此,TypeScript 可以进行类型编程,这会极大提高 TypeScript 在复杂场景下的应用场景。

function identity0(args:any):any{
    return args
}

我们需要返回值的类型和参数一致,所以我们在函数名之后使用 <> 定一个泛型 T,你可以理解这个 T 的意思就是给函数参数定义了一个类型变量,会在后面使用,相当于【type T = arg 的类型】,返回值使用 T 这个类型就完成了这个需求。

function identity<T>(arg:T):T{
    // console.log(arg)
    return arg
}
identity<string>("vue")
identity<number>(111)

有了泛型之后,我们就有了把函数参数定义成类型的功能,我们就可以实现类似高阶函数的类型函数。

interface vueCourse5{
    name:string,
    price:number
}

keyof 可以帮助我们拆解已有类型

type courseProps = keyof vueCourse5
// let k:courseProps = true
let k1:courseProps = "name"
let k2:courseProps = "price"

使用 extends 来实现类型系统中的条件判断。

type ExtendsType<T> = T extends boolean ? string:number
let et:ExtendsType<string> = 123 //ExtendsType<string>===number
type ExtendsType1 = ExtendsType<string> // number
let et1:ExtendsType1 = 123
type ExtendsType2 = ExtendsType<true> // string
let et2:ExtendsType2 = "123"

extends 相当于 TypeScript 世界中的条件语句,然后 in 关键字可以理解为 TypeScript 世界中的遍历。

type courses = 'name'|'price'
type courseObj={
    [k in courses]:number
}
let co:courseObj = {
    'name':123,
    'price':12333
}

function getProperty<T,K extends keyof T>(o:T,name:K):T[K]{
    console.log(o[name])
    return o[name]
}
const coursePrice:courseObj={
    'name':1,
    'price':2
}
getProperty(coursePrice,'price')

让我们拥有了给函数的参数定义类型变量的能力,infer 则是可以在 extends 之后的变量设置类型变量,更加细致地控制类型。

type Foo =()=>courseObj
type returnType<T>= T extends () =>infer P?P:string
type Foo1 = returnType<Foo>
type Foo2 = returnType<string>

let f:Foo1={
    'name':1,
    'price':2
}
let f2:Foo2="string"

实战练习

interface Todo{
    title:string ,
    desc:string,
    done:boolean
}
type todo1 = keyof Todo
let td1:todo1 = "done"
type partTodo = Partial1<Todo>

我们需要实现类型函数 Partial1,返回的类型是 Todo 所有的属性都变成可选项。

type Partial1<T> = {
    [K in keyof T]?:T[K]
}


//从两种type中筛选出不相同的类型
type Exclude1<T,K> = T extends K ?never:K
let ex:Exclude1<string,number> =111
let ex1:Exclude1<string,Todo>


type Pick1<T,K extends keyof T>={
    [P in K]:T[P]
}
type pick2 = Pick1<partTodo,todo1>

例1:

        sectionData: [
          {
              title: ["title1", "title2"],
              content: [
                  { icon: accountIcon, title: "account", path: "/account" },
                  {
                      icon: searchIcon,
                      title: "search",
                      path: "/search",
                      info: "search",
                      num: 0,
                  }, 
                  {
                      icon: record,
                      title: "record",
                      path: "/record",
                      info: "record",
                  },
              ],
          },
        ]
        

ts 定义类型

        class SectionDataType{
            title:string[],
            content:Array<contentType>=[]
        }
        interface contentType{
          icon: string,
          title: string,
          path: string,
          info?: string,
          num?: number,
        }