Typescript初体验

Typescript

Typescript 官网地址: https://www.typescriptlang.org/zh/

使用 nvm 来管理 node 版本: https://github.com/nvm-sh/nvm

装 Typescript:

npm install -g typescript

使用 tsc 全局命令:

// 查看 tsc 版本
tsc -v
// 编译 ts 文件
tsc fileName.ts

1.原始数据类型和Any类型

//布尔类型
let isDone: boolean = true
//数字类型
let age: number = 123
//字符串类型,模板字符串也可以
let firstName = 'Kiwi'
let message = `hello ${firstName}`
//undefined 和 null
let u: undefined = undefined
let n: null = null

//注:undefined 和 null 是所有类型的子类型
// let num: number = undefined
// 但是在新版的 ts 中,有一个特殊的配置是 strictNullChecks,
// 在有些编辑器中默认为 true。它会严格检查 null 以及 undefined 类型,它只能赋值给本身的类型或者 any。

//any:允许赋值为任意类型
let notSure: any = 4
notSure = 'maybe a string'
notSure = true
//在其上还可以任意调用方法和属性,在我们有明确类型时,应避免使用any
notSure.myName
notSure.geyName()

2.数组和元组

数组的声明方式如下,以纯数字数组举例

let arrayOfNumbers: number[] = [1, 2, 3]
arrayOfNumbers.push(3)
arrayOfNumbers.unshift(4)
//arrayOfNumbers.push('hello')	会报错

与js一样ts也可以使用一些数组的方法,但如果在方法里传入的参数与声明的类型不同的话,是会报错的

补充:我们都知道在js里有一种特殊的数据结构——类数组(array-like Objects),它很像数组,但是和数组不一样

//类数组
function test() {
    console.log(arguments);
    arguments.length
    arguments[0]
    // let arrat: any[] = arguments   不能把一个数组赋值给arguments
}

其具备数组的一些属性,如length,并且可以取其索引,但是它没有数组上的一些方法,并且我们不能把一个数组赋值给他,说明arguments和数组不是一个类型

其实在ts里给arguments专门定义了一个类型,叫IArguments

在这里插入图片描述

同理ts还有很多其他的内置类型,在这里不做细讲,后面如果有用到再会过来复习

元祖tuple:一定程度上限制了数据类型的数组

简单理解定义了具体索引的具体数据类型的数组

//元组
let user: [string, number] = ['kiwi', 21]

数据类型不对应或者数组内容多了或少了都会报错

但可以通过push()方法突破这个限制,但是只能添加规定好的类型中的一种(上述例子即只能添加string和number中的一种)

user.push(123)
user.push('我饿了')
// user.push(true)	报错

3.Interface- 接口 初探

interface 对对象(shape)进行描述

IPerson 中I用于提示定义的是一个interface,我这里没加

readonly 和 const 的区别: readonly用于定义对象的属性只读,const 用于定义的变量只读

interface Person {
    //readonly只读属性,只能在初始化时修改
    readonly id: number
    name: string
    age: number
    //可选属性  字面意思
    sex?: string
}

let kiwi: Person = {
    id: 1,
    name: 'kiwi',
    age: 21,
    sex: '男'
}
kiwi.age = 23
// kiwi.id = 2  readonly属性不能二次修改

let mjy: Person = {
    id:2,
    name: 'mjy',
    age: 21,
}

4.函数

// function add(x: number, y: number, z?: number): number {
//     if (typeof z == 'number') {
//         return x + y + z
//     } else {
//         return x + y
//     }
// }

//函数表达式写法
const add = (x: number, y: number, z?: number): number => {
    if (typeof z == 'number') {
        return x + y + z
    } else {
        return x + y
    }
}
//let add2:string=add赋值不适配赋值函数需要按照函数的方式进行生命
// 不是函数声明的情况下 给变量赋值表达式返回值需要使用箭头
let add2: (x: number, y: number, z?: number) => number = add

//interface在函数中的使用
interface ISum {
    (x: number, y: number, z?: number): number
}
let add3: ISum = add

说实话有点没理解这个赋值函数类型的意义在哪里。。

这里当我们把一个变量赋值给一个函数的时候,这个变量就自动获得了一个类型,如下图所示,至于为什么下一节给大家解释

在这里插入图片描述

5.类型推论、联合类型和类型断言

上一节我们发现把一个变量赋值给一个函数的时候,这个变量就自动获得了一个类型,这其实是ts的一个原则——类型推论

类型推论:ts会自动推断当前变量的类型,如赋值无关类型则会报编译错误

例:

let num = 123
// num = '123'报错

之前我们学了any类型,any是一个大而全的数据类型,在不到万不得以的情况下我们不会使用它,但是现在假设有一种情况我们需要允许一部分的类型可以使用,比如说一个变量可以是字符串也可以是数字,其他类型则不行,那么这种情况我们该怎么处理呢

这里我们引入一个新的数据类型——联合类型(union types)

//union type
let numberOrStirng: number | string
numberOrStirng = 123
numberOrStirng = '123'

注意:联合类型的变量只能访问此联合类型所有类型里共有的属性或方法,但有的时候我们又确实要在不确定变量类型的情况下访问对于的属性或方法

这里我们就要使用到类型断言

类型断言:将一种不确定的变量类型,告诉ts编译器,就是某一种变量类型。可欺骗编译器但避免不了运行时的报错(使用as关键字)

例:假设我们现在要一个getLength()函数,要求其输入的类型可以是number也可以是string,但返回的类型只能是number(因为获取的是长度)

function getLength(input: number | string): number {
    const str = input as string
    if (str.length) {
        return str.length
    } else {
        const number = input as number
        return number.toString().length
    }
}

注意:类型断言不是类型转换,如果在这里定义一个变量将其断言为联合类型中不存在的数据类型的话,是会报错的

除了使用类型断言可以实现以外,我们还可以使用类型守卫type-guard来实现(使用typeof关键字)

function getLength(input: number | string): number {
    if (typeof input == 'string') {
        return input.length     //这里的input是string类型
    } else {
        return input.toString().length  //这里的input自动变成了number类型
    }
}

6.Class类

面向对象编程的三大特点

  • 封装(Encapsulation):将对数据的操作细节隐藏起来,只暴露对外的接口。外界调用端不需要(也不可能)知道细节,就能通过对外提供的接口来访问该对象,
  • 继承(Inheritance):子类继承父类,子类除了拥有父类的所有特性外,还有一些更具体的特性。
  • 多态(Polymorphism):由继承而产生了相关的不同的类,对同一个方法可以有不同的响应。
class Animal {
  name: string;
  constructor(name: string) {
    this.name = name
  }
  run() {
    return `${this.name} is running`
  }
}
const snake = new Animal('lily')

// 继承的特性
class Dog extends Animal {
  bark() {
    return `${this.name} is barking`
  }
}

const xiaobao = new Dog('xiaobao')
console.log(xiaobao.run())
console.log(xiaobao.bark())

// 这里我们重写构造函数,注意在子类的构造函数中,必须使用 super 调用父类的方法,要不就会报错。
class Cat extends Animal {
  constructor(name) {
    super(name)
    console.log(this.name)
  }
  run() {
    return 'Meow, ' + super.run()
  }
}
const maomao = new Cat('maomao')
console.log(maomao.run())

类成员的访问修饰符:

  • public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的
  • private 修饰的属性或方法是私有的,不能在声明它的类的外部访问
  • protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的

7.类与接口

用类实现一个接口

interface Radio {
  switchRadio(trigger: boolean): void;
}
class Car implements Radio {
  switchRadio(trigger) {
    return 123
  }
}
class Cellphone implements Radio {
  switchRadio() {
  }
}

interface Battery {
  checkBatteryStatus(): void;
}

// 要实现多个接口,我们只需要中间用 逗号 隔开即可。
class Cellphone implements Radio, Battery {
  switchRadio() {
  }
  checkBatteryStatus() {

  }
}

8.enums枚举

enum Direction {
    Up,
    Down,
    Left,
    Right
}

console.log(Direction.Up);	//0

// 还有一个神奇的点是这个枚举还做了反向映射
console.log(Direction[0])	//Up

enum Direction {
    Up = 10,
    Down,
    Left,
    Right
}
console.log(Direction.Down);	//11

// 字符串枚举
enum Direction1 {
    Up = 'UP',
    Down = 'DOWN',
    Left = 'LEFT',
    Right = 'RIGHT',
}
const value = 'UP'
if (value === Direction1.Up) {
    console.log('go up!')
}

//常量枚举constenum编译时直接编译成常量,提高性能
const enum Direction3 {
    Up,
    Down,
    Left,
    Right
}

9.泛型 Generics

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

function echo(arg) {
  return arg
}
const result = echo(123)
// 这时候我们发现了一个问题,我们传入了数字,但是返回了 any

function echo<T>(arg: T): T {
  return arg
}
const result = echo(123)

// 泛型也可以传入多个值
function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]]
}

const result = swap(['string', 123])

10.约束泛型

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法

interface搭配泛型之后,可以灵活的返回不同的类型

创建一个拥有特定类型的容器,仿佛给一个容器贴标签一样

泛型就好像一个可变的参数,在用的时候传入,生成这个不同类型的一个容器,或者像上节课讲的用它来灵活的约束参数的类型,不需要参数是一个特别死板的类型,不希望他是一个特定string、number类型,我要传入的参数必须有某某属性、某某方法,否则就会报错。

在函数使用的时候,函数的这个类型推断,不会流入到函数体内,所以使用表达式,没法建立明确的绑定,用泛型可以让我们打破这个鸿沟,这个时候就可以返回它传入的类型。

class Queue {
  private data = [];
  push(item) {
    return this.data.push(item)
  }
  pop() {
    return this.data.shift()
  }
}

const queue = new Queue()
queue.push(1)
queue.push('str')
console.log(queue.pop().toFixed())
console.log(queue.pop().toFixed())

//在上述代码中存在一个问题,它允许你向队列中添加任何类型的数据,当然,当数据被弹出队列时,也可以是任意类型。在上面的示例中,看起来人们可以向队列中添加string 类型的数据,但是那么在使用的过程中,就会出现我们无法捕捉到的错误,

class Queue<T> {
  private data = [];
  push(item: T) {
    return this.data.push(item)
  }
  pop(): T {
    return this.data.shift()
  }
}
const queue = new Queue<number>()

//泛型和 interface
interface KeyPair<T, U> {
  key: T;
  value: U;
}

let kp1: KeyPair<number, string> = { key: 1, value: "str"}
let kp2: KeyPair<string, number> = { key: "str", value: 123}

11.类型别名、字面量、交叉类型

类型别名,其实对于js就是将一个表达式赋给一个新变量,目的就是为了,能减少重复代码量,

对于联合类型新的理解,就是或类型,意思就是可以是多个类型中的一个;也要加type关键字要不然和普通变量之间无法区别,自己尝试的时候忘加了type关键字,这个要注意

交叉类型:就是与的关系,但这个有点特殊它不是在选交集,它是多种类型同时满足的意思,用**关键字&**来连接;它可以配合接口来使用

//类型别名
//type-aliase
let sum: (x: number, y: number) => number
// const result = sum(1,2)

type PlusType = (x: number, y: number) => number
let sum2: PlusType
let result2 = sum2(1, 2)

type StrOrString = string | number
let res: StrOrString = 123
res = '123'

//字符串字面量
const str1: 'name' = 'name'
const number1: 1 = 1
type Direction = 'Up' | 'Down' | 'Left' | 'Right'
let toUp: Direction = 'Up'

//交叉类型
interface IName {
    name: string
}
type person = IName & { age: number }
let me: person = { name: 'yjt', age: 21 }

12.声明文件

声明文件后缀:xxx.d.ts

它里面没有任何的实际实现代码,只有类型声明

可以用来查一些第三方库:https://types.kubajastrz.com/package/vue

声明文件一般是用于非ts编写的项目,即原来是js模块现在想用ts项目中在次使用

下面我们用一个小例子来体会一下

calculator.d.ts

//字面量
type IOperator = 'plus' | 'minus'
//传入两个参数,
//第一个看起来像是一个字符串,其实是一个限制了输入的字面量,限制只能输入 plus 或者 minus 
//第二个输入的是一个数组,表示用于计算的数字
//type ICalculator = (operator: IOperator, numbers: number[]) => number
interface ICalculator {
    (operator: IOperator, numbers: number[]): number	//函数类型使用冒号
    plus
}
declare const calculator: ICalculator 
//模块导出
export default calculator

13.内置类型

const a: Array<number> = [1,2,3]
// 大家可以看到这个类型,不同的文件中有多处定义,但是它们都是 内部定义的一部分,然后根据不同的版本或者功能合并在了一起,一个interface 或者 类多次定义会合并在一起。这些文件一般都是以 lib 开头,以 d.ts 结尾,告诉大家,我是一个内置对象类型欧
const date: Date = new Date()
const reg = /abc/
// 我们还可以使用一些 build in object,内置对象,比如 Math 与其他全局对象不同的是,Math 不是一个构造器。Math 的所有属性与方法都是静态的。

Math.pow(2,2)

// DOM 和 BOM 标准对象
// document 对象,返回的是一个 HTMLElement
let body: HTMLElement = document.body
// document 上面的query 方法,返回的是一个 nodeList 类型
let allLis = document.querySelectorAll('li')

//当然添加事件也是很重要的一部分,document 上面有 addEventListener 方法,注意这个回调函数,因为类型推断,这里面的 e 事件对象也自动获得了类型,这里是个 mouseEvent 类型,因为点击是一个鼠标事件,现在我们可以方便的使用 e 上面的方法和属性。
document.addEventListener('click', (e) => {
  e.preventDefault()
})

Typescript 还提供了一些功能性,帮助性的类型,这些类型,大家在 js 的世界是看不到的,这些类型叫做 utility types,提供一些简洁明快而且非常方便的功能。

// partial,它可以把传入的类型都变成可选
interface IPerson {
  name: string
  age: number
}

let viking: IPerson = { name: 'viking', age: 20 }
type IPartial = Partial<IPerson>
let viking2: IPartial = { }

// Omit,它返回的类型可以忽略传入类型的某个属性

type IOmit = Omit<IPerson, 'name'>
let viking3: IOmit = { age: 20 }

14.配置文件 tsconfig.json

官方文档:https://www.typescriptlang.org/tsconfig

  • files:运行tsc时会编译括号内的文件
  • compilerOptions:怎么编译?
    • outDir:编译好的文件应该输出放到哪里,默认是当前文件夹
    • module:指要输出的module的类型
    • target:选择要符合的ES标准
    • declaration:true自动生成目录下文件的声明文件
{
  "files": ["test.ts", "test2.d.ts"],	
  "compilerOptions": {
    "outDir": "./output",
    "module": "ESNext",
    "target":"ES5",
    "declaration": true
  }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/402530.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

外包干了三年,技术算是废了。。。

先说一下自己的个人情况&#xff0c;大专生&#xff0c;17年通过校招进入湖南某软件公司&#xff0c;干了接近5年的手工测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了五年的手工…

力扣基础刷题---二分查找

704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 中心思想&#xff1a;找到中间值&#xff0c;跟中间值比…

PCIE1—快速实现PCIE接口上下位机通信(一)

1.简介 PCI Express&#xff08;PCIE&#xff09;是一种高速串行总线标准&#xff0c;广泛应用于计算机系统中&#xff0c;用于连接主板和外部设备。在FPGA领域中&#xff0c;PCIE也被广泛应用于实现高速数据传输和通信。FPGA是一种灵活可编程的集成电路&#xff0c;可以根据需…

时序数据库TimescaleDB,实战部署全攻略

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

bat 查找文件所在

脚本 在批处理文件&#xff08;.bat&#xff09;中查找文件所在的目录&#xff0c;你可以使用dir命令结合循环和条件语句来实现。以下是一个简单的示例&#xff0c;演示如何在批处理文件中查找指定文件并输出其所在目录&#xff1a; echo off setlocal enabledelayedexpansio…

绩效域-错题笔记

1、虚荣指标&#xff1a;对决策没有帮助的度量指标一般属于虚荣指标。 例如&#xff1a;新访问者的数量比网站的页面访问量更加有用。 2、完工偏差(VAC)用于预测预算赤字或盈余金额&#xff0c;它表示为完工预算(BAC)和完工估算(EAC)之差。 3、完工尚需绩效指数(TCPI)用于估…

pikachu靶场-CSRF

CSRF: 介绍&#xff1a; Cross-site request forgery简称为"CSRF”。 在CSF的攻击场景中攻击者会伪造一个请求&#xff08;这个请求一般是一个链接&#xff09; 然后欺骗目标用户进行点击&#xff0c;用户一旦点击了这个请求&#xff0c;整个攻击也就完成了&#xff0…

Java毕业设计-基于ssm的校园二手交易管理系统-第67期

获取源码资料&#xff0c;请移步从戎源码网&#xff1a;从戎源码网_专业的计算机毕业设计网站 项目介绍 基于ssm的校园二手交易管理系统&#xff1a;前端jsp、jquery&#xff0c;后端 springmvc、spring、mybatis&#xff0c;集成商品管理、订单管理、销售管理、采购管理、购…

phaseDNN文章解读

文章DOI: https://doi.org/10.48550/arXiv.1905.01389 作者是 Southern Methodist University 的Wei Cai 教授 A Parallel Phase Shift Deep Neural Network for Adaptive Wideband Learning 一种并行移相深度神经网络来自适应学习宽带频率信号 20190514 核心思想&#xff1a;…

客户端web开发工具

文章目录 安全网络Linter-->捕获代码错误-->eslint源代码控制-->Git代码格式化-->Prettier打包工具--Parcel--Webpack 转换--Babel开发后阶段测试工具配置工具其他 node&#xff0c;npm、yarnnode.js包管理器npmyarn https://developer.mozilla.org/zh-CN/docs/Lea…

RM电控讲义【HAL库篇】

这段代码中do while的作用&#xff1a; 宏定义中的语句块&#xff1a;do { ... } while (0) 允许你在宏定义中创建一个语句块&#xff0c;从而可以包含多条语句。这在宏定义中特别有用&#xff0c;因为宏只是简单的文本替换&#xff0c;不像函数那样有作用域和返回类型。因此&…

Docker基础篇(-)

docker 三个要素 镜像容器仓库 CentOS 6.8 安装 docker centos 7.0 yum install -y yum-utils device-mapper-persistent-data lvm2 yum-config-manager -y --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo systemctl start docker 启动Docker&…

嵌入式中c语言指针用法详解与分析

今天给大家来讲解一下指针。 我会由浅到深&#xff0c;最后结合实际应用讲解&#xff0c;让大家学会指针的同时&#xff0c;知道大佬们都用指针来干嘛&#xff01; 长文预警&#xff01;全文大约5200多字&#xff0c;学指针看这篇文章就够了&#xff01; 很多人跟我刚学习c语…

electorn+vue3项目启动后报错unsafe-eval,如何去除提醒

electron项目报错如下&#xff1a; Electron Security Warning (Insecure Content-Security-Policy) This renderer process has either no Content Security Policy set or a policy with “unsafe-eval” enabled. This exposes users of this app to unnecessary security r…

【FreeRTOS基础入门】任务通知

文章目录 前言一、任务通知介绍1.1 任务通知怎么通信1.2 任务通知与其他通信方式的区别1.3 优势及限制任务通知的优势任务通知的限制 1.4 内部原理 二、任务通知的使用2.1 发出与接收通知简化版2.1 发出与接收通知专业版 总结 前言 FreeRTOS 提供了丰富而灵活的任务通知机制&a…

基于51单片机的婴儿看护监测系统[proteus仿真]

基于51单片机的婴儿看护监测系统[proteus仿真] 婴儿看护检测系统这个题目算是课程设计和毕业设计中少见的题目&#xff0c;本期是一个基于51单片机的婴儿看护监测系统[proteus仿真] 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 2&a…

openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制

文章目录 openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制226.1 适用场景226.2 非适用场景 openGauss学习笔记-226 openGauss性能调优-系统调优-配置LLVM-LLVM适用场景与限制 226.1 适用场景 支持LLVM的表达式 查询语句中存在以下的表达式支持…

优化设备维修流程:易点易动设备管理系统的设备维修管理策略

在现代企业中&#xff0c;设备是生产运营的核心要素之一。然而&#xff0c;设备故障和维修常常给企业带来诸多困扰和成本。为了提高设备维修的效率和质量&#xff0c;许多企业开始采用先进的设备管理系统。在这方面&#xff0c;易点易动设备管理系统以其卓越的设备维修管理策略…

2023年Q4,SSD出货量下降5%,但存储容量增长9.6%

2023年第四季度&#xff08;4Q23&#xff09;的SSD市场表现呈现出单位出货量下降&#xff0c;但存储容量增长的趋势。具体数据显示&#xff0c;该季度SSD总出货量下降5%&#xff0c;降至8820万台&#xff1b; 而存储容量则增长9.6%&#xff0c;达到85.1EB。预计2023全年SSD总容…

前端基于Verdaccio搭建私有npm仓库,上传npm插件包,及下载使用自己的npm插件包

文章目录 一、原理二、常用的仓库地址三、优势四、准备环境六、使用verdaccio搭建私有npm服务1、安装2、运行3、配置config.yaml&#xff0c;使局域网下能共享访问&#xff0c;否则只能本机访问。4、重新运行 七、npm常见操作查看当前用户信息查看源地址切换源地址删除源地址创…