0.Ts基础

0.0 【如何初始化 tsconfig.json文件】

  • 通过在命令行终端输入 tsc --init 可以快速初始化 一个名叫 tsconfig.jsonTs 配置文件

    0.1【如何监听 ts 文件的变化自动生成对应的 js 文件】

  • 通过在命令行终端输入 tsc --watch 可以监听当前 .ts 的改变,自动生成向对应的 .js 文件

    0.2【ts 中的基本类型】

  • stringnumberbooleanArrayany

    0.4【ts 中的类型注释】

    1. let myAge: number = 20 // :number 便是变量 myAge 的类型注释
    2. let myName = 'Youwei' // 也可不用显示的指定其变量的类型注释,TypeSCript 会根据值类型自动推断

    0.5【ts 中函数的类型注释】

    ``typescript function getName(name: string): string{ console.log(my name is ${name}`) return name }

// 在上面函数中,括号内参数可以也可以书写类型注释,括号后的 :string 表示该函数返回值的类型注释

  1. <a name="szr7s"></a>
  2. #### 0.6【上下文类型】
  3. ```typescript
  4. const food = ['肉夹馍','凉皮','冰峰']
  5. food.forEach((item)=>{
  6. item.toUpperCase()
  7. })
  8. // 观察上面函数,可以发现,函数的参数 item 并没有指定类型注释,但依然可以使用字符串才有的方法
  9. // 这是因为 TypeSCript 会自动根据 food 参数的类型与 forEach 函数的上下文类型中,自动推断出参数 item 的类型

0.7【对象参数类型】

  1. function printCoord(PT: {X: number, Y: number}) {
  2. console.log(`坐标X的值为:${PT.X}`);
  3. console.log(`坐标Y的值为:${PT.Y}`);
  4. }
  5. printCoord({X:300, Y:400})
  6. //可以将函数的参数的类型注释指定为一个对象传入

0.8【可选参数】

  1. function getPersonName(person: { name: string, age?: number }) {
  2. console.log(`姓名为:${person.name}`);
  3. console.log(`年龄为:${person.age}`);
  4. }
  5. getPersonName({name: '柳白猿'})
  6. // 上面函数的参数 age?: number 表示该参数是一个可传可不传的参数
  7. // 当我们调用 getPersonName 函数时不传入 age 参数也不会触发 TypeScript 的类型检查

0.9【联合类型】

联合类型可以让我们变量或参数类型变得宽泛,但如果需要使用该变量或者参数时,就一定要让其满足多种类型的具备的方法或属性

  1. // id:number | string 表示联合类型,即此时 id 的类型可以为 number类型 或者 string类型
  2. let id: number | string = '001x'
  3. id = '100' // 正确
  4. id = 100 // 正确
  5. id = {uid: 100} // 错误
  6. // --------------------------------------------------------------------------------//
  7. function getName(name: string[] | string){
  8. // 当我们给 name 参数直接使用 数组的方法时,会报错。因为 name 不一定是数组类型,有可能只是一个 string 类型
  9. // name.join() 直接使用会报错
  10. // 此时我们可以使用 if 语句进行判断,来达到目的
  11. if(Array.isArray(name)){
  12. return name.join('浩克')
  13. }else{
  14. return name.toUpperCase()
  15. }
  16. }
  17. getName(['mark42', '奇异博士', 'Tony'])
  18. getName('绯红女巫')
  19. getName(20) // 报错
  20. // 函数 getName 方法接收一个 name 参数,而 name 参数的类型可以是一个 string类型的数组,或者 string

0.10【类型别名】

使用 type 关键字来定义类型别名

  1. // 定义变量 ponit 为对象类型
  2. type point = {
  3. X: number,
  4. Y: number
  5. }
  6. function getPoint(point: point){
  7. console.log(`当前X位置为${point.X}`);
  8. console.log(`当前Y位置为${point.Y}`);
  9. }
  10. getPoint({X: 500, Y: 100}) // 正确
  11. getPoint(Y:100) // 报错
  12. // 定义变量 ID 的类型为 number类型的数组或 string类型
  13. type ID = number[] | string
  14. function getId(id: ID) {
  15. return id.slice(0,2)
  16. }
  17. getId([100,200,300]) // 正确
  18. getId('100') // 正确
  19. getId({id: '100'}) // 报错
  20. // 定义变量 returnType 的类型为 string 或 number类型
  21. type returnType = string | number
  22. type value = number | string
  23. function setSumVal(value): returnType {
  24. return value
  25. }
  26. setSumVal(20) // 正确
  27. setSumVal('1000') // 正确
  28. setSumVal([100,200,'300']) // 报错

0.11【接口 interface】

使用 interface 关键字来定义接口

  1. interface IPonit{
  2. X: number,
  3. Y: number
  4. }
  5. function getPoint(point: point){
  6. console.log(`当前X位置为${point.X}`);
  7. console.log(`当前Y位置为${point.Y}`);
  8. }
  9. getPoint({X: 500, Y: 100})

0.12【接口与类型别名的区别】

  • 接口使用 interface 关键字定义,类型别名使用 type 关键字定义,两者都是定义类型注释
  • 类型别名 type 使用 & 来扩展新的属性,interface 接口则通过 extends 继承老接口属性,来扩展新属性
  • 同一个接口可以支持重复定义,并添加新的属性,但是同一个类型别名不支持重复定义

    0.12.1【接口的扩展】
  • 接口的扩展可以使用 extends 关键字,让新接口在不修改老接口的情况下,添加新的属性 ```typescript interface IPerson{ name: string, age: number }

// 此时的 ISkill 接口中除了有 skill 属性还有,继承自 IPerson 接口的 name 属性与 age 属性 interface ISkill extends IPerson{ skill: string }

// 此时的变量 Tony 的类型必须同时满足 IPerson 与 ISkill 两个接口的定义 const Tony: ISkill = { name: ‘Tony’, age: 30, skill: ‘钞能力’ } // 在上面的栗子中可以看到, ISkill 接口通过 extends 继承了 IPerson 接口,并且定义了自己的新属性 // 在使用 ISkill 作为变量 Tony 的类型注释时就必须要满足 IPerson 与 ISkill 两种接口的定义

  1. <a name="ze8XM"></a>
  2. ##### 0.12.2【类型别名的扩展】
  3. - 类型别名通过 `&` 来基于旧的类型别名,扩展新的属性
  4. ```typescript
  5. type IAnimal = {
  6. name: string
  7. age: number
  8. }
  9. // 此时的 IPanda 中不仅有 hobby,还有扩展自 IAnimal 的 name 与 age 属性
  10. type IPanda = IAnimal & {
  11. hobby: string
  12. }
  13. // 变量 pandaInfo 必须同时满足 类型别名 IAnimal 与 IPanda 中定义的属性
  14. const pandaInfo: IPanda = {
  15. name: '大宝',
  16. age: 3,
  17. hobby: '睡觉'
  18. }
  19. // 上面的栗子,IPanda 通过 & 基于 IAnimal 扩展了自己新的属性 hobby
  20. // 在使用时,变量 pandaInfo 使用了类型注解 IPanda,就需要同时满足 IAnimal 与 IPanda 的定义

0.12.3【重复定义】
  1. interface IPerson{
  2. name: string
  3. }
  4. interface IPerson{
  5. age: number
  6. }
  7. interface IPerson{
  8. hobby: string
  9. }
  10. const person: IPerson = {
  11. name: 'Tony',
  12. age: 30,
  13. hobby: 'honey'
  14. }
  15. // 以上的接口 IPerson 被重复定义多次,并不会报错
  16. // 并且在使用 IPerson 作为类型注解时,需要满足接口的所有定义
  17. // -----------------------------------------------------------------------------//
  18. type IAnimal = {
  19. name: string
  20. }
  21. type IAnimal = {
  22. age: string
  23. }
  24. type IAnimal = {
  25. skill: string
  26. }
  27. 类型别名不支持重复定义,会报错:标识符“IAnimal”重复

0.13【断言】

  • 使用 as可以更具体的指定某一属性或变量的类型,类型断言由编译器删除,不会影响代码运行时的行为,所以也没有与 as 相关的运行时检查 ```typescript const canvas1 = document.getElementById(‘myCanvas’) as HTMLCanvasElement

const canvas2 = document.getElementById(‘myCanvas’)

// 通过上面的栗子,获取了id 名为 myCanvas 的元素,但 TypeScript 只会返回某种类型的 HTMLElement // 但是并不知道具体是哪一种类型的 HTMLElement 元素,这时候可以使用 as 语法,指定一个明确的类型

  1. <a name="r4H8S"></a>
  2. #### 0.14【文字类型】
  3. - 可以使用 文本 或者 数字 作为变量或参数类型
  4. <a name="H70YG"></a>
  5. ##### 0.14.1【字符串文字类型】
  6. ```typescript
  7. let personName: 'Tony' = 'Tony'
  8. personName = 'zs' // 报错:不能将类型“"zs"”分配给类型“"Tony"”
  9. // 上面的栗子中,将字符串 'Tony' 设置为变量 personName 的类型注解,当赋值不是 字符串的 'Tony' 是便会报错
  10. //----------------------------------------------------------------------------------------//
  11. // 一般用配合联合类型使用,文字联合类型,达到一个更具体的概念
  12. function getLocation(name: string, location: 'left' | 'top' | 'right'){
  13. console.log(`名称是${name}, 位置在${location}`);
  14. }
  15. getLocation('Tony', 'right') // 正确
  16. getLocation('Hulk', 'top') // 正确
  17. getLocation('zs', '左上角') // 报错:类型“"bottom"”的参数不能赋给类型“"left" | "top" | "right"”的参数
  18. // getLocation 函数的第二个参数 location 此时只允许传入 'left' | 'top' | 'right',这几种字符串
  19. // 相当于限制死了 location 参数的值

0.14.2【数值文字类型】
  1. // 数值文字类型与字符串文字类型类似
  2. function compare(a: string, b: string): 1 | 0 | -1{
  3. // return a === b ? 0 : a > b ? 1 : 2 报错:不能将类型“2”分配给类型“0 | 1 | -1”
  4. return a === b ? 0 : a > b ? 1 : -1
  5. }
  6. // 在上面的栗子中,已经规定了函数的返回值必须是 1 | 0 | -1
  7. // 如果函数 compare 的返回值不是 1 | 0 | -1 其中的一种,便会报错

0.14.3【文字类型与其他类型的结合】
  1. interface IOption {
  2. width: number
  3. }
  4. function setValue(value: IOption | 'auto'){
  5. return value
  6. }
  7. setValue({width: 200})
  8. setValue('auto')
  9. setValue(30) // 报错:类型“30”的参数不能赋给类型“IOption | "auto"”的参数

0.14.4【布尔文字类型】
  1. let flag1: true = true
  2. let flag2: false = false
  3. flag1 = false // 报错:不能将类型“false”分配给类型“true”
  4. flag2 = true // 报错:不能将类型“true”分配给类型“false”
  5. // 上面的栗子:相当于对 flag1 和 flag2 设置了一个固定的布尔值类型注释,重新赋值会报错

0.14.5【文字推理】
  1. let obj = {
  2. value: 0
  3. }
  4. obj.value = 0
  5. obj.value = '0' // 报错:不能将类型“string”分配给类型“number”
  6. // TypeScript 基于上下文,帮我们推断出了 value 的类型为 number,
  7. // 当赋值数字类型的 0 时可以成功,赋值为字符串类型 '0' 即报错
  8. // -----------------------------------------------------------------------------------//
  9. const request = {
  10. url: 'http:xxxxxx.com',
  11. method: 'GET'
  12. }
  13. function getDataList(url: string, method: 'GET'| 'POST'){
  14. return `url是:${url}, 请求方法是${method}`
  15. }
  16. getDataList(request.url, request.method) // 报错:类型“string”的参数不能赋给类型“"GET" | "POST"”的参数
  17. // 上面函数栗子:参数 method 的类型只能是 'GET'| 'POST',其实传入的值是对的
  18. // 但是 TypeScript 帮我们自动推断 request.method 的值为 string 类型,导致类型判断失败报错
  19. // 解决方法:
  20. // 1. getDataList(request.url, request.method as 'GET') // 正确,使用类型断言解决
  21. // 2. const request = {
  22. // url: 'http:xxxxxx.com',
  23. // method: 'GET'
  24. // } as const 给整个 request 对象,断言为 const 将类型固定

0.15【null和undefined类型】

  1. let Y: undefined = undefined
  2. let X: null = null
  3. function getName(x? number | null){
  4. console.log(x!.toFixed()) // 使用非空断言
  5. }
  6. // 在上述的栗子中,对变量 x 使用 !,表示非空断言,即 x 的值一定存在,将 null 与 undefined 排除在外

0.16【枚举类型】

  • 枚举类型通过 enum 关键字来定义,后面跟一个枚举的名字与类型定义
  • 默认情况下,枚举成员是基于 0 开始递增,当显式的设置了类型类型的值,则会根据值,返回相应的结果 ```typescript enum Food { noodles , Hamburger, rice, }

Food.noodles // 0 Food.Hamburger // 1 Food.rice // 2 // ————————————————————————————————— enum Fruits { apple = 1, // 显式的设置了枚举类型的值 banana, strawberry, grape, }

Fruits.apple // 1 Fruits.banana // 2 Fruits.strawberry // 3 ….

  1. <a name="Zns8q"></a>
  2. #### 0.17【类型缩小】
  3. - `TypeScript` 类型收窄就是从宽类型转换成窄类型的过程,类型收窄常用于处理联合类型变量的场景。
  4. <a name="USweb"></a>
  5. ##### 0.17.1【typeof 类型守卫】
  6. - `typeof`可以获取 `Ts` 代码在执行时,有关值最基本的类型信息
  7. ```typescript
  8. function printAll(strs: string | string[] | null) {
  9. if (Array.isArray(strs) ) {
  10. // strs 为数组时 TODO
  11. for (const value of strs){}
  12. } else if (typeof strs === 'string') {
  13. // strs 为字符串时 TODO
  14. } else {
  15. // 为 null 时 TODO
  16. }
  17. }
  18. // 在上面栗子中,首先通过 Array.isArray() 方法判断 strs 是否为一个数组 strs 为数组时命中
  19. // 接下来通过 typeof 判断 strs 的值是否为字符串,当 strs 为字符串命中
  20. // 若以上都未命中,则 strs 的值为 null

0.17.2【真值缩小】
  • 通过 条件、&&、||、if语句、布尔否定( ! ) 组成一个布尔表达式

    1. function multiplyAll(values: number[] | undefined, factor: number){
    2. if(!values){
    3. return values
    4. }else{
    5. return values.map(() => {
    6. return x * factor
    7. })
    8. }
    9. }
    10. multiplyAll([8.9],2)
    11. // 通过 ! 取反 values 变量,组成一个布尔表达式,若逻辑命中做一些事情,未命中进行其他操作

    0.17.3【等值缩小】
  • ===!====!= 来处理等值检查,实现类型缩小

    1. function example(x: string | number, y: string | boolean){
    2. if( x === y){
    3. x.toUpperCase()
    4. y.toUpperCase()
    5. }else{
    6. // TODO...
    7. }
    8. }
    9. // 上面的栗子中,由于函数参数 x 和 y 共同存有类型注释 string 所以 if 会成立

    0.17.4【in操作符缩小 】
  • 使用 in 运算符,用来确定某一对象是否有某个名称的属性,以此缩小潜在类型的范围

image.png

  1. type Fish = { swim: () => void }
  2. type Bird = { fly: () => void }
  3. function move(animal: Fish | Bird) {
  4. if ('swim' in animal) {
  5. return animal.swim()
  6. }
  7. return animal.fly()
  8. }
  9. // 在上面的栗子中:animal 的类型注释是 Fish 与 Bird 的联合类型
  10. // 因此在 if 语句的判断中 'swim' 是存在于 animal 中的

0.17.5【分配缩小】
  1. let x = Math.random() < 0.5 ? 10 : 'hello world'
  2. // 现在变量 x 现在相当于拥有 number | string 的联合类型,let x :number | string
  3. let x = 1000 // 正确 满足 let x :number 类型
  4. let x = 'Yooooo' // 正确 满足 let x :string 类型
  5. let x = [] // 报错
  6. let x = {} // 报错

0.18【never类型】

  • never 类型表示不应该存在的状态
  • never 类型可以分配给每个类型,但是除 never本身以外,没有任何类型可以分配给 never类型
    1. let x: never
    2. x = 'hellow', // 报错:不能将类型“string”分配给类型“never”
    3. x = 20 // 报错:不能将类型“number”分配给类型“never”

    0.19【函数】

    0.19.1【函数类型表达式】
    1. fn: (a:string) => void
    2. // 上面的栗子就是一段函数表达式类型
    3. // fn 函数后面有点类似箭头函数的样子的便是 fn 函数的类型
    0.19.2【函数重载】

    函数重载或方法重载,是使用相同名称和不同参数、数量或类型创建多个方法的一种能力

  1. // 为 add 函数提供了多个函数类型定义,从⽽实现函数的重载
  2. type Combinable = string | number
  3. function add(a: number, b: number): number;
  4. function add(a: string, b: string): string;
  5. function add(a: string, b: number): string;
  6. function add(a: Combinable, b: Combinable) {
  7. // type Combinable = string | number;
  8. if (typeof a === 'string' || typeof b === 'string') {
  9. return a.toString() + b.toString();
  10. }
  11. return a + b;
  12. }