typeScript学习笔记(一)

学习资源来自:

类与接口 · TypeScript 入门教程 (xcatliu.com) 

一.TypeScript的安装和运行

1.安装TypeScript

  • 通过npm(Node.js包管理器)
  • 安装Visual Studio的TypeScript插件:(Visual Studio 2017和Visual Studio 2015 Update 3默认包含了TypeScript。 如果你的Visual Studio还没有安装TypeScript)

2.npm安装TypeScript

  • 安装教程 TypeScript环境搭建,并且部署到VSCode(亲测有效)_咖啡壶子的博客-CSDN博客
  • 安装过程中问题:

TypeScript- 解决(tsc 不是内部或外部命令,也不是可运行的程序或批处理文件)问题 - sanyekui - 博客园 (cnblogs.com)

3.将环境安装好之后,就可以尝试构建第一个typeScript程序

  • 新建Demo.ts文件
function greeter(person: string) {
  return 'Hello, ' + person
}

let user = 'Jane User'

document.body.innerHTML = greeter(user)
  • 在终端输入: 
    tsc greeter.ts
  • 就会输出一个 包含和输入内容一样的JavaScript代码

      typeScript学习笔记(一)_第1张图片

  • 新建文件demo.html 


    TypeScript Greeter
    
        
    
  •    执行结果会展示在界面上

二.基础类型

1.布尔值

  • true 或者 false
  • 在JS或者TS中叫boolean
  • 语法: let boolean = false
  • 注意:使用Boolean 创造的对象不是布尔值,比如:let createdByNewBoolean: Boolean = new Boolean(1);
  • 直接调用Boolean 也可以返回一个 boolean类型 let createdByBoolean: boolean = Boolean(1);
  • 在TypeScript中,boolean 是基本类型,Boolean 是构造函数 

2.数字类型

  • TypeScript里面所有数字都是浮点数,这些浮点数的类型是Number
  • 除了支持十进制和十六进制字面量,TypeScript还支持ECMAScript 2015 中引入的二进制和八进制
  • 二进制表示法:let binaryLiteral: number = 0b1010;
  • 八进制表示法:let octalLiteral: number = 0o744;
  • 二进制和八进制会被编译为十进制数字      

3.字符串

  • 使用string表示
  • 用单引号或者双引号包起来
  • 可以使用模板字符串 ${`XXX`}

4.数组

  • let list:number[] = [1, 2, 3]
  • let list:Array = [1, 2, 3]

5.元组Tuple

  • 表示一个已知元素数量和类型的数组
  • 各个元素的类型不一定相同
  • let x:[string, number]
  • x = ['hello', 10]

6.枚举

  • enum类型是对JavaScript标准数据类型的一个补充
enum Color {
  Red,
  Green,
  Blue,
}
let c: Color = Color.Green
  • 默认情况下,从0开始为元素编号,可以手动指定成员的数值

enum Color {
  Red = 1,
  Green,
  Blue,
}
let c: Color = Color.Green
console.log(c) // 2
  • 全部采用手动赋值

enum Color {
  Red = 1,
  Green = 2,
  Blue = 4,
}
let c: Color = Color.Blue
console.log(c) // 4
  • 找出映射的名字

enum Color {
  Red = 1,
  Green,
  Blue,
}
let colorName: string = Color[2]
console.log(colorName) // Green

7.任意值 any

  • 需要在编译阶段还不清楚的变量指定一个类型
  • 不希望类型检查器对这些值进行检查,而是直接让它们通过编译阶段的检查
  • 就可以使用any类型来标记这些变量
  • 注意:Object类型的变量只是允许你给他赋任意值,却不能够在它上面调用任意的方法
  • 当你只知道一部分数据的类型时,any类型也是有用的, 比如:
    let list: any[] = [1, true, "free"]
  • 声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值

  • 在声明的之后,未指定其类型,就会被识别为任意值类型

8.空值 void

  • 表示没有任何类型
  • 当一个函数没有返回值时,就会见到void
  • 声明一个void变量没什么用,因为你只能赋予它undefined 和 null

9.null和undefined

  • 与void的区别是,undefined和null是所有类型的子类型,也就是说undefined类型的变量可以赋值给所有类型
  • void的类型不可以

10.never

  • never类型标识永不存在的值的类型
  • never类型是那些总是会抛出异常,或者根本不会有返回值的函数表达式或箭头函数表达式的返回值类型
  • 变量也可能是never类型,当它们被永不为真的类型保护所约束的时候
  • never类型是任何类型的子类型:可以赋值给任何类型
  • 没有类型是never的子类型,就是只能将never类型赋值给never类型
  • any也不可以赋值给never
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
    throw new Error(message);
}

// 推断的返回值类型为never
function fail() {
    return error("Something failed");
}

// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
    while (true) {
    }
}

12.联合类型

  • 表示取值可以为多种类型中的一种
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
  • 联合类型使用 | 分隔每个类型

  • let myFavoriteNumber: string | number;  的含义是允许 myFavoriteNumber 是string类型或者number类型,但是不能是其他类型

  • 当TypeScript不确定一个联合类型的变量到底是哪个类型的时候,只能访问此联合类型中所有属性里共有的属性和方法

11.类型断言

  • 类型断言就相当其他语言中的类型转换(显示转换)
  • 但是不进行特殊的数据检查和解构
  • 没有运行时的影响,只是在编译阶段起作用

  两种形式:

  • 尖括号语法
let someValue: any = 'this is a string'
let strLength: number = (someValue).length
  • as 语法
let someValue: any = 'this is a string'
let strLength: number = (someValue as string).length

12.类型推论

下面代码虽然没有指定类型,但是在编译的时候会报错,TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论

let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
  • 如果在定义的时候没有赋值,不管之后有没有赋值,都会被推断为any类型而不被类型检查

三.变量声明

① var和let声明与JavaScript中一致

  • var 声明可以在包含它的函数,模块,命名空间或者全局作用域内部任何位置被访问
  • let 支持块作用域,在声明之前读或者写
  • var可以多次声明,只会取最后一个
  • let如果多次声明就会报错
  • 使用let替换var

② const 声明

  • 赋值后不能被改变
  • 和let的作用域规则相同,但是不能对它们重新赋值
  • 但是可以修改对象内部的值

③ let 和 const 的选择

  • 使用最小特权原则,所有变量除了你计划去修改的都应该使用const
  • 如果需要修改就使用let

④ 解构

解构数组

  • 最简单的解构数组
let input = [1, 2]
let [first, second] = input
console.log(first)
console.log(second)
  • 利用解构交换两个变量的值

let input = [1, 2]
let [first, second] = input
;[first, second] = [second, first]

console.log(first)
console.log(second)
  • 解构用在函数参数上
let input = [1, 2]
function f([first, second]: number[]) {
  console.log(first)
  console.log(second)
}
f(input)
  • 使用剩余参数解构

let [first, ...rest] = [1, 2, 3, 4]
console.log(first)
console.log(rest)
  • 只解构其中一个数据
let [first] = [1, 2, 3, 4]
console.log(first)
  • 将不关心的元素省略

let [, second, , fourth] = [1, 2, 3, 4]
console.log(second)

  解构对象

let o = {
  a: 'foo',
  b: 12,
  c: 'bar',
}
let { a, b } = o
console.log(a)
console.log(b)
  • 属性重命名

let o = {
  a: 'foo',
  b: 12,
  c: 'bar',
}
let { a: name1, b: name2 } = o
console.log(name1)
console.log(name2)
  • 默认值

typeScript学习笔记(一)_第2张图片

  • 参数默认值(如果函数没有传参数,就使用初始化列表中的值)

typeScript学习笔记(一)_第3张图片

  • 解构表达还是要尽量保持小而简单

⑤ 展开运算符

  • 允许将一个数组展开为另一个数组
let first = [1, 2]
let second = [3, 4]
let bothPlus = [0, ...first, ...second, 5]
console.log(bothPlus) // [0, 1, 2, 3, 4, 5]
  • 将一个对象展开为另一个对象
let defaults = { food: 'spicy', price: '$$', ambiance: 'noisy' }
let bothPlus = { ...defaults, food: 'rich' }
console.log(bothPlus) //

四.接口

  • 在TypeScript中,使用接口(Interfaces)来定义对象的类型
  • 在TypeScript中,除了可用于对类的一部分行为进行抽象以外,也常用于对【对象的形状】进行描述
  • TypeScript核心原则:对值所具有的结构进行类型检查
  • 接口的作用是为这些类型命名和为你的代码或第三方代码定义契约
  • 下面的例子:定义了一个接口,接着定义了一个变量tom,类型是Person,  约束tom的形状必须和接口 Person 一致
  • 定义的变量比接口少了一些属性或者多了一些属性都不被允许,必须和接口的形状保持一致

      typeScript学习笔记(一)_第4张图片

typeScript学习笔记(一)_第5张图片 

  • 可选属性

    ① 有时候希望不要完全匹配一个形状,可以用可选属性

    ② 可选属性的含义就是该属性可以不存在

    ③ 但是仍然不允许添加未定义的属性

  typeScript学习笔记(一)_第6张图片

  • 任意属性  

typeScript学习笔记(一)_第7张图片

     ① 有时候我们希望一个接口允许有任意的属性,上面是使用 [propName: string]  定义了任意属性取string 类型的值

    ② 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

    ③ 所以下面的案例中,如果任意属性中的值允许是string, 但是可选属性age的值是number,不是string的子属性,所以报错了

typeScript学习笔记(一)_第8张图片

④ 一个接口中只能定义一个任意属性,如果接口中有多个类型的属性,则可以在任意属性中使用联合类型

typeScript学习笔记(一)_第9张图片

  • 只读属性

  ① 有时候我们希望对象中的一些字段只能在创建的时候被赋值,就使用 readonly 定义只读属性

  ② 只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候

  ③ 只读属性不能进行赋值(无法修改)

interface Point {
  readonly x: number
  readonly y: number
}

let p1: Point = { x: 10, y: 10 }
p1.x = 10   // 会报错,属性是只读属性

③ 只读属性在第一次初始化对象的时候,必须进行初始化

typeScript学习笔记(一)_第10张图片 

  • 数组只读属性

let a: number[] = [1, 2, 3, 4]
// 限制数组是只读类型
let ro: ReadonlyArray = a

ro[0] = 1 // 报错
  • readonly 和 const

    ① 作为变量使用的话就用const

    ② 作为属性就使用  readonly

  •  额外的属性检查

typeScript学习笔记(一)_第11张图片

解决方法 ① 

typeScript学习笔记(一)_第12张图片

解决方法 ② :添加一个字符串索引签名

typeScript学习笔记(一)_第13张图片

解决方法③ 将这个对象赋值给另一个变量,不会经过额外属性检查

typeScript学习笔记(一)_第14张图片

五.数组类型

   ①【类型 + 方括号】表示法

let fibonacci: number[] = [1, 1, 2, 3, 5];

  ② 数组的项中不允许出现其他类型

     

  ③ 数组中的一些方法的参数也会根据数组在定义时约定的类型进行限制

   typeScript学习笔记(一)_第15张图片

④ 可以使用数组泛型   Array  来表示数组

let fibonacci: Array = [1, 1, 2, 3, 5];

⑤ 用接口表示数组

interface NumberArr {
  [index: number]: number
}
let fib: NumberArr = [1, 2, 3, 4]

   用接口表示数组比较少见,但是经常用它表示类数组(伪数组)

    typeScript学习笔记(一)_第16张图片

使用普通数组不能定义为数组,所以使用接口的形式

   typeScript学习笔记(一)_第17张图片

常用的类数组都有自己的接口定义,比如 IArgumentsNodeListHTMLCollection

function sum() {
  let args: IArguments = arguments
}

IArgument 是TypeScript定义好的类型,实际上就是

interface IArguments {
    [index: number]: any;
    length: number;
    callee: Function;
}

⑥ any在数组中的应用:any表示数组中允许出现任意类型

let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];

六.函数的类型

① 函数声明:

  • 输入多余或者少于的参数,是不被允许的
function sum(x: number, y: number): number {
    return x + y;
}

②  函数表达式

let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

TypeScript 中的 => 和 ES6中的箭头不一样,TypeScript中,=>用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型

③ 用接口定义函数的形状

interface SearchFunc {
  (source: string, subString: string): boolean
}

let mySearch: SearchFunc
mySearch = function (source: string, subString: string) {
  return source.search(subString) !== -1
}
  • 用函数表达式或者接口定义函数时,对等号左侧进行类型限制,可以保证对函数名赋值时保证参数的个数,参数类型和返回值类型不变

④ 可选参数

  • 在参数名后面加问号
  • 可选参数后面不允许再出现必选参数

typeScript学习笔记(一)_第18张图片

typeScript学习笔记(一)_第19张图片

⑤ 参数默认值

  • 允许给函数添加默认值,TypeScript会将添加了默认值的参数识别为可选参数
  • 不受【可选参数必须在必选参数后面】限制了

typeScript学习笔记(一)_第20张图片

⑥ 剩余参数

  • ...rest的方式获取函数中的剩余参数
  • rest只能是最后一个参数
function push(array: any[], ...items: any[]) {
  items.forEach(function (item) {
    array.push(item)
  })
}

let a: any[] = []
push(a, 1, 2, 3)

⑦ 重载

  • 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理
  • 注意:多个函数定义如果有包含关系,需要优先把精确的定义写在前面,因为TypeScript会优先从最前面的函数定义开始匹配
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('')
  }
}

七.类型断言

① 概念

  • 类型断言含义:可以手动指定一个值类型

  • 语法:值 as 类型     <类型>值

  • 在 tsx(React 的 jsx 语法的 ts 版)  语法中必须使用前者

  • <类型>这种语法在ts中除了表示类型断言之外,也有可能是表示一个泛型

  • 所以在使用断言的时候,统一使用 值 as 这样的语法

② 类型断言的用途

(1) 将一个联合类型断言为其中一个类型

typeScript学习笔记(一)_第21张图片

  • 此时可以使用类型断言,将 animal 断言成 Fish

typeScript学习笔记(一)_第22张图片

  • 使用断言的时候一定要格外小心,尽量避免断言后调用方法或者引用深层属性,减少不必要的运行时的错误

(2)将一个父类断言为更加具体的子类

typeScript学习笔记(一)_第23张图片

(3)将任何一个类型断言为any

typeScript学习笔记(一)_第24张图片

typeScript学习笔记(一)_第25张图片

  • 在any类型的变量上,访问任何属性都是允许的
  • 但是它可能掩盖了真正的类型错误,所以如果不是非常确定,就不要使用as any
  • 不能滥用 as any, 也不要完全否定它的作用,需要在类型的严格性和开发的便利性之间掌握平衡

(4)将as断言为一个具体的类型

  • 明确tom的类型之后,提高代码的可维护性

typeScript学习笔记(一)_第26张图片

总结

  • 联合类型可以被断言为其中一个类型
  • 父类可以被断言为子类
  • 任何类型都可以被断言为any
  • any可以被断言为任何类型

③ 类型断言的限制

  • 并不是任何一个类型都可以被断言为任何另一个类型
  • 如果A兼容B,那么A能够被断言成B, B也能被断言成A
interface Animal {
  name: string
}
interface Cat {
  name: string
  run(): void
}

function testAnimal(animal: Animal) {
  return animal as Cat
}
function testCat(cat: Cat) {
  return cat as Animal
}
  • 允许animal as Cat 是因为 [父类可以被断言为子类]
  • 允许cat as Animal 是因为既然子类拥有父类的属性和方法,那么被断言为父类,获取父类的方法,就不会有任何问题,所以子类可以被断言为父类
  • 要使A能够被断言成B, 只需要A兼容B或者B兼容A即可

综上所述:

  • 联合类型可以被断言为其中一个类型
  • 父类可以被断言为子类
  • 任何类型都可以被被断言为any
  • any可以被断言为任何类型
  • 要使得 A 能够被断言为 B,只需要 A 兼容 B 或 B 兼容 A 即可

④ 双重断言

interface Cat {
  run(): void
}
interface Fish {
  swim(): void
}

function testCat(cat: Cat) {
  return cat as any as Fish
}
  • 会导致运行时错误,除非迫不得已,千万别使用双重断言

⑤ 类型断言和类型转换

  • 类型断言只会影响到TypeScript编译时的类型,类型断言语句在编译结果中会被删除

typeScript学习笔记(一)_第27张图片

  • 类型断言不是类型转换,它不会真的影响到变量的类型
  • 如果要进行类型转换,需要直接调用类型转换的方法

 typeScript学习笔记(一)_第28张图片

 ⑥ 类型断言和类型声明

类型断言

typeScript学习笔记(一)_第29张图片

typeScript学习笔记(一)_第30张图片 

  • animal断言为Cat,只需要满足Animal 兼容 Cat 或 Cat 兼容 Animal 即可
  • animal赋值为Cat, 需要满足Cat兼容Animal才行,但是Cat并不兼容Animal
  • 类型声明比类型断言更加严格,最好优先使用类型声明

⑦ 类型断言和泛型

  •  更加规范的实现对 getCacheData 返回值的约束
  • 同时去掉了代码中的any,是最优的一个解决方案
function getCacheData(key: string): T {
  return (window as any).cache[key]
}

interface Cat {
  name: string
  run(): void
}

const tom = getCacheData('tom')
tom.run()

八. 声明文件

① 当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全,接口提示等功能

② 声明语句

③ 声明文件:把声明语句放到一个单独的文件中

文件以 .d.ts 为结尾 ,其他所有*.ts文件就可以获得 jQuery 的类型定义了

declare var jQuery: (selector: string) => any;

typeScript学习笔记(一)_第31张图片

④ 第三方声明文件

  • 使用 @type 统一管理第三方库的声明文件
  • 直接使用npm 安装对应的声明模块 
    npm install @types/jquery --save-dev

⑤ 书写声明文件

库的使用场景:

  • 全局变量:通过

你可能感兴趣的:(typescript,学习,笔记)