TypeScript 快速入门

TypeScript 学习笔记

JS 自有类型系统的问题

1. 语言类型的分类

强类型与弱类型

编程语言的划分从安全的维度划分
强类型:变量类型定义之后不会任意进行隐式类型转换 (JAVA)
弱类型:会进行隐式转换 (JS)
强类型优势

  • 错误更早暴露。如:编译时就知道代码的错误了
  • 代码更智能,编码更准确。如:编辑器知道变量类型,会实时进行提示
  • 减少不必要的类型判断。

静态类型与动态类型

编程语言从类型检查的维度划分
静态类型:变量在声明的时候其类型就是明确的,声明过后他的类型是不允许进行随意的修改的
动态类型:变量是没有类型的,变量的类型取决于代码运行时值的类型

2. JS自有类型系统的问题

JAVASCRIPT 属于弱类型&动态类型语言
弱类型&动态类型语言的缺点

  • “任意”
  • “不靠谱”
  • 由于JS是属于脚本语言,不需要进行编译,其执行依赖运行环境。代码错误往往都是运行时才能够发现错误,而不能够在编译的时候就知道错误的所在。
// 弱类型的问题
// 进行隐式转换,使函数功能发生改变了
// 这样的结果不是我们期待的结果,我们期待的时求两个数的和
function add(num1, num2) {
	return num1 + num2
}

add(100, '200') // 100200

JS 为什么会设计成弱类型&静态类型语言哪?
这是历史遗留问题,早期的web应用都是非常简单的应用,不需要太多代码就可以实现一个应用,如果在有类型检查会显得复杂且多余。但现在随着web应用越来越复杂庞大,之前的语言设计就显得力不从心。

JavaScript 类型系统问题解决方案

  • Flow FACEBOOK团队推出的JS类型检查器来进行类型检查 Flow的文章
  • 使用 TypeScript

TypeScript

TypeScript的认识

  • 根据前面的点我们知道JavaScript语言设计的不足在于其是弱类型&动态类型的语言,不会进行类型检查.不适合于开发大型应用.
  • TypeScript是为了解决JavaScript自有类型系统的问题而诞生的,TypeScript是JavaScript的超集.
  • TypeScript中也可以写ES6+的语法,在编译的时候会转换为目标,相当于会有个babel的功能

快速上手

快速上手查看官方文档
TypeScript中文官方手册
当运行 tsc 对ts代码进行编译的时候
在这里插入图片描述

TypeScript 的语法

1. TS 六种原始类型

// TypeScript 原始数据类型
// Number
const num: number = 1234
const str: string = '1234' 
const _undefined: void = undefined
const _boolean: boolean = true
const _null: null = null
const _symbol: symbol = Symbol()

//  需要注意的点是:
// 1. 非严格模式下 变量可以为null || undefined || 所定义的类型
// 严格模式下 定义的变量只能够是所定义的类型
// 2. tsconfig配置文件默认编译代码是ES5,所使用的标准库也是ES5的,ES5的ts标准库没有对ES6内置对象进行定义,所以使用定义symbol类型会出现错误
// 解决办法修改tsconfig中lib配置为['ES2015', 'DOM']。
// DOM标准库包含BOM DOM 
// 所谓的标准库就是TS对JS内置对象,api的定义,可以理解为代码检查规则配置文件。

在这里插入图片描述

2. 让TS以中文提示错误消息

  • 运行npx tsc --locale zh-CN
  • vs code 中 修改设置里面找到 typescript locale配置

3. TS 作用域问题

如下图描述

test.ts中定义了a变量
在这里插入图片描述
test1.ts中同样定义a变量
在这里插入图片描述
解决办法就是让每一个文件形成自己的作用域.
在这里插入图片描述

4. TS中的Object 类型(除了原始类型以外的其他类型)

export { }
// object类型指原始类型一外的其他类型
const a: object = function () { }
const b: object = / /

// 1. object 对象类型的限制可以使用 字面量的形式进行限制
const obj: { name: string, age: number } = { name: 'reborn', age: 18 }

// 2. 数组类型
// 表示定义的数组必须都是数字类型
const arr: number[] = [1, 2, 3]
// 泛型定义数组, 这个与上面是相同的意思
const arr1: Array<number> = [1, 2, 3]

// 3. 元组类型的定义
// 元组含义是明确元素数量以及明确元素类型的数组
// 如下:定义个三个元素,第一个为number类型,第二个为string类型,第三个为boolean类型
const tuple: [number, string, boolean] = [1, '1', false,]

// 4. 枚举类型
// 1. 通过enum关键字可以定义枚举数据结构,枚举数据结构一般都是定义一些常量的,如下面定义文章的状态类型。
const enum postStatus {
  draft = 0,
  unpublished = 1,
  published = 2
}
console.log(postStatus['draft'])
// 上面代码会被编译成如下代码,定义了一组以对象值作为键的数据,一组以对象键作为键
// postStatus {draft: 0, 0: draft, unpublished: 1, 1: unpublished, published: 2, 2: published}
// (function (postStatus) {
//   postStatus[postStatus["draft"] = 0] = "draft";
//   postStatus[postStatus["unpublished"] = 1] = "unpublished";
//   postStatus[postStatus["published"] = 2] = "published";
// })(postStatus || (postStatus = {}));
// 2. 另外一点可以直接定义常量枚举,只需要在 enum关键字前面加上 const,这样编译出来的代码直接是枚举值了。
// console.log(0 /* 'draft' */);

// 注意的点:
// 枚举数据可以分为 字符串枚举与 数字枚举
// 数字枚举可以不用定义值,默认会从 0 开始,如上面postStatus {draft,unpublished,published}, 会得到 {draft = 0 。。。}
// 数字枚举定义第一个值,后面的值会默认补全。如上面postStatus {draft = 8,unpublished,published}, 会得到 {draft = 8 , unpublished = 9,published = 10 }
// 字符串枚举必须要定义相应的值

// 5. 函数类型
// 1) 函数字面量声明
// fn1 函数的形参必须是两个number 类型,该函数的返回值必须是number类型
function fn1(num1: number, num2: number): number {
  return num1 + num2
}

// 2) 函数表达式声明
// 函数表达式会有一个变量fn2接受一个函数,当前变量也可以进行类型定义,表面接受什么样的函数
// 如fn2表示,它只能够接受一个为字符串的参数,且没有返回值的函数
const fn2:(greeting: string) => void = (greeting: string): void => {
  console.log(`${greeting} Reborn~`)

// 6. Date类型
const date: Date = new Date()
}

// 7. RegExp 
const reg: RegExp = /^/

5. TS 中定义任意类型

// Any类型
// 表示,hello函数可以接受任意类型参数。
// any 类型不会有TS类型检查,一般是用于兼容老的代码。
function hello(greeting: any): void {}


6. TS 的隐式类型推断机制

TS 具有隐式类型推断机制.下图我们并没有给变量 a 添加上类型注解, 但给 a 变量赋值了number类型的2,此时 TS 就会推断改数据类型为number类型,当我们再次给a赋值字符串’3423’的时候,VsCode就会有报错提示.

虽然说隐式类型推断机制能够让我们不用去写类型注解为我们开发提供方便,但是不建议采用次机制.
在这里插入图片描述

7. TS 类型断言

// TS 的类型断言
// TS 类型断言的应用场景
// 1. 假设arr是来自 后台接口 返回的数据
const arr = [1,2,3,4]

// 2. 我们需要查找 为 2 的数据
const target = arr.find( item => item === 2)

// 4. 这时候可以采用类型断言,告诉 TS 我们确认改类型为 number
// - 采用 as 关键字
const result = target as number
// - 采用泛型也可以进行类型断言 , 不过再写 jsx 的时候泛型的尖括号会与html标签进行冲突
// const result = <number> target

// 3. 此时就不能够进行 * 运算,因为target 类型有可能为 number 有可能为 undefined
const res = result * result

8. TS 接口

一句话用来约束对象的结构(成员的类型, 成员个数),在实际的编译时并不会编译为JS代码.

// TS 接口 
// 用来约束对象成员

// 1. 通过interface关键字约定接口
interface Post {
  title: string
  content: string
  subTitle?: string
  // 2. 可选成员, 只读成员, 动态成员
  // - 通过 ? 定义 可选成员
  // - 通过 readonly 关键字定义只读成员
  // - 通过 对象的 key 的计算属性定义 动态成员
  readonly summary: string
}

function printPost(post: Post): void {
  console.log(post.title)
  console.log(post.content)
  console.log(post.summary)
}

printPost({title: 'reborn', content: 'vergood', summary: '哈哈哈哈'})


 // - 通过 对象的 key 的计算属性定义 动态成员
 // 设置动态成员,就可以为对象添加任意成员,
//  下面接口表示可以为对象添加任意成员, 类型约束为了 key 为str, value str
interface dongtai {
  [dd: string]: string
}

const ojb: dongtai = {
  hello : 'hello',
  age: '18'
}

TS 中定义的接口不会进行编译成JS
在这里插入图片描述

9. TS 中的 类

const { log } = console
// TS 中类的使用
// 1. 为实例添加属性的时候,需要先对属性进行声明
// 3. 可以给类的成员定义访问修饰符如 private 定制私有属性,public定义 公有属性(默认) protected 受保护的(只允许在子类访问该成员)
class Person {
  // 2. ES2016 可以直接在对象中通过 变量名= 值 的形式为 实例赋值属性
  public name: string
  // name?:string = undefined

  // 6. private 关键字定义的变量仅适用于当前 类的内部使用
  private age: number
  protected gender: boolean

  // 7. 通过添加 readonly 关键字 标注对象是只读属性

  public readonly weight:number = 180

  // 4. 给构造函数定义为公有类型,可以在外部通过new 关键字创建实例,如果为 private 只能够在构造函数内部创建实例
  public constructor(name: string, age: number, gender: boolean) {
    this.name = name
    this.age = age
    this.gender = gender
    
  }

  public sayHi():void {
    log(this.age)
  }
}

class Student extends Person {
  public constructor(name: string, age: number) {
    super(name, age, false)
    // 5. protected 关键字定义的属性仅适用于子类继承访问
    log(this.gender, 'protected')
  }
}


10. 类中的接口

const { log } = console
// 实现类的结构
// 需求: 
// 现在需要定义两个类 Person Animal 类, 都有相同行为 吃 与 行走等行为,此时我们可以通过接口进行抽象公共方法

// 定义类的接口

// 表示该数据类型要有 eatFood 和 walk 的行为(方法)
interface EatAndWalk {
  eatFood(food: string): void
  walk(where: string): void
}


class Person implements EatAndWalk {
  public eatFood(food: string): void {
    log(`eat ${food}`)
  }

  public walk(where: string): void {
    log(`${where} on street `)
  }
}

class Animal implements EatAndWalk {
  public eatFood(food: string): void {
    log(`eat ${food}`)
  }

  public walk(where: string): void {
    log(`${where} on street `)
  }
}

11. 抽象类

// 抽象类
// 我所理解的抽象类更像是某一类事物的源头(祖先类),如 动物类是 就是抽象类,小猫就是他的子类。
// 抽象类仅适用于继承不能够通过 new 关键字去创建实例, 仅用于子类的继承
// 通过 abstract 定义
abstract class Animal {
  eat(food: string): void {
    console.log(`eat ${food}`)
  }
  // 也可以定义抽象方法,当父类有抽象方法的时候,其子类也必须要有所定义的方法。
  abstract run(distance: number): void
}

class Cat extends Animal {
  constructor(food: string) {
    super()
    this.eat(food)
  }
  run(distance: number): void {

  }

}

// let animal = new Animal() // TS会报错

12. 泛型

// 泛型
// 在声明的时候不去指定具体数据类型,在调用的指定数据的类型
// 请看下面的例子
// 给createArra 声明了一个泛型类型T, 并将所有不明确的类型都定义成T,在函数调用的时候指定该类型是什么类型的。
function createArra<T>(length: number, value: T): T[] {
  const arr = Array<T>(length).fill(value)
  return arr
}

const resNumberArr = createArra<number>(3, 100)
console.log(resNumberArr)

const resStringArr = createArra<string>(3, '333')
console.log(resStringArr)

13. TS 类型声明

import { upperCase } from 'lodash'
// TS 类型声明
// 如果引入的第三方模块不是 ts模块的的话,类型系统会失效,必须要自己手动声明方法类型
// 1. 声明 upperCase方法
declare function upperCase(str: string):string

// 2. 此时调用upperCase就会进行类型检查
const res = upperCase('hello')

在这里插入图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值