TypeScript 学习笔记 环境安装-类型注解-语法细节-类-接口-泛型

news2024/11/25 20:14:19

文章目录

  • TypeScript 学习笔记
    • 概述
      • TypeScript 开发环境搭建
    • 类型注解
      • 类型推断
    • 数据类型
      • JS的7个原始类型
      • Array数组
      • object、Object 和 {}
        • 可选属性 ? 和 可选链运算符?.
      • function函数
      • TS类型: any类型 | unknow类型
      • TS类型: void类型
      • TS类型:never类型 (几乎不用)
      • TS类型:Tuple(元组)
      • 语法细节
        • 1.联合类型 | 和交叉类型 &
        • 2.type和interface接口作为类型声明
        • 3.类型断言 as 和非空类型断言
        • 4.字面量类型和类型缩小
        • 5.函数的类型和函数签名
          • 函数表达式
          • 函数调用签名:支持声明属性
          • 构造签名(理解)
        • 6.函数的重载和this类型
          • 函数的重载(通用库和工具中使用较多)
          • this类型(很少使用)
          • this相关的内置工具(了解)
        • 7.函数的参数
        • 可选参数
          • 默认参数
          • 剩余参数
    • TypeScript 类的使用
      • 类的特性
      • 抽象类abstract
    • TypeScript对象类型
      • 索引签名
      • 函数签名
    • TypeScript接口
      • 类实现接口 implements
      • 抽象类和接口的区别
      • 特殊:严格字面量赋值检测
    • TypeScript枚举类型
    • 泛型
      • 泛型语法的基本使用
      • 泛型约束和类型条件
        • keyof 和 in
      • TypeScript映射类型(开发中很少用,用于封装组件)
        • 映射类型修饰符

TypeScript 学习笔记

概述

JavaScript的变量类型相当于是动态类型,可以跟随着赋值的改变而类型改变,函数的参数也没有设定类型,所以在定位错误以及安全性上不太够。

说明
1.TS不能被JS解析器直接执行,需要编译成JS执行
2.即使TS编译出错也可以编译成JS

1.TypeScript是什么?

TypeScript 是类型安全的JavaScript(拥有类型的JavaScript超集),JavaScript是弱类型, 很多错误只有在运行时才会被发现,而TypeScript提供了一套静态检测机制, 在编译时就发现错误

2.TypeScript的好处是什么?
1.支持强类型
2.类型注解:通过类型注解来增加编译时静态类型检查
3.增加了特性:泛型、接口、抽象类等

在这里插入图片描述

TypeScript 开发环境搭建

安装TS解析器 :解析器使用nodejs写的 —> 先安装nodejs我node已经安装好啦,直接安装TypeScript

1.使用npm全局安装TypeScript

npm i -g typescript

2.检查是否安装成功

tsc --version

3.创建一个ts文件:后缀以ts结尾
使用tsc对ts文件进行编译,进入到该文件的命令行,使用tsc 文件名进行编译

tsc test.ts

TS运行效果查看
1.通过tsc编译TS代码到JS代码
2.在浏览器环境或者node环境下运行JS代码

简化版本

  • 通过webpack 配置本地的TS编译环境和开启一个本地服务,可以直接运行在浏览器上(ts-loader)
  • 通过ts-node库,为TS的运行提供执行环境
# 安装ts-node
npm install ts-node -g
# 按照ts-node依赖包tslib @types/node
npm install tslib @types/node - g
# node上运行js
node xxx.js
# node上运行ts
ts-node xxx.ts

类型注解

类型注解:主动告诉ts是什么类型
语法:变量名:数据类型
说明
1.冒号后面的数据类型被称为类型注解★
2.声明了类型后TS就会进行类型检测

类型推断

类型推断:声明一个变量时,如果有直接赋值,会根据赋值的类型推导出标变量的类型注解。

说明
1.声明变量如果不指定类型,也不赋值,则TS解析器会自动判断变量的类型为any
2.从右到左推断
3.let进行类型推导,推导出来是通用类型
const类型推导,推导出来是字面量类型

const height = 1.88 //1.88为heigth的类型,1.88是字面量类型
const height:1.88 //等价于

数据类型

  • JS数据类型: 7个原始类型 (string,number,boolean,undefined,null,bigint,symbol) + Array + Object
  • TS新增类型: any + unknow +void + never

总结
1.默认情况下 nullundefined 是所有类型的子类型,这两种类型可以赋值给任意类型的变量
4.never类型是任何类型的子类型,也可以赋值给任何类型的变量
2. unknown 是 顶级类型,任何类型的值(never除外)都可以赋值给该类型的变量
3. any 是顶级类型,任何类型的值(never除外)都可以赋值给该类型的变量,也是所有类型的子类型,any相当于关闭类型检测

JS的7个原始类型

let str: string = "jimmy";
let num: number = 24;
let bool: boolean = false;
let u: undefined = undefined;
let n: null = null;
let big: bigint = 100n;
let sym: symbol = Symbol("me"); 

说明
1.默认情况下 nullundefined 是所有类型的子类型。 可以把 null 和 undefined 赋值给其他类型。

如果你在tsconfig.json指定了"strictNullChecks":true ,null 和 undefined 只能赋值给 void 和它们各自的类型。

2.String是JavaScript中字符串包装类

Array数组

//明确指定数组的类型注解写法1
let arr:string[] = ["1","2"];
//明确指定数组的类型注解写法2 泛型写法
let arr2:Array<string> = ["1","2"]

object、Object 和 {}

语法:{属性名:属性值,属性名:属性值....}
说明
1.属性个数名字必须一模一样,不能多不能少
2.分割符分号和逗号可以,换行可以省略分号,因为JS中换行会自动添加分号,最好使用逗号。
3.object、Object 和 {}

  • Object 代表所有拥有 toStringhasOwnProperty 方法的类型,所有原始类型、非原始类型都可以赋给 Object。在严格模式下,null 和 undefined 类型也不能赋给 Object。
  • {}等价于Object
  • object 则表示非原始类型
let info: object = {
      name: 'why',
      age: 18
    }
info = [1,2,3]; //非原始类型
console.log(info);
info = 123; //报错

4.类型不注解,默认为any


以下写法,在实际开发中并不会使用,因为object并不清楚里面的属性是什么类型,获取到对象里的属性会报错。

//不使用
let info: object = {
  name: 'why',
  age: 18
}
console.log(info['age']) //报错

/*
写法1
let info: {
name:string
age:number
}
写法2
let info: {name:string;age:number}
写法3
let info: {name:string,age:number}
*/
let info: {
name:string
age:number
} = {
  name: 'why',
  age: 18
}

可选属性 ? 和 可选链运算符?.

可选属性?
如果某一属性不确定,可以使用?标识该属性可选
语法:{属性名:属性值,属性名?:属性值}

可选链运算符?.
?.用来判断左侧的表达式是否是 null | undefined,如果是则会停止表达式运行,可以减少我们大量的&&运算。

a === null || a === void 0 ? void 0 : a.b;
function getData(data: any){
	let name = data?.row?.name //data && data.row ? data.row.name:undefined
}
//普通写法
function getData(data: any){
	let name;
	if (data && data.row) {
		name = data.row.name
	}
}

比如我们写出a?.b时,
理解1:a ? a.b :undefined
理解2:如果a为null或者undefined,则后面的表达式等于没有,如果存在则获取a.b

访问属性的时候可以用可选链,但是赋值时不可以使用比如: xxx?.xxx=xxx

function函数

function add(x: number, y: number): void {
     x + y;
}

1.匿名函数 - 大多数时候不需要类型注解
当一个函数出现在TS可以确定该函数会被如何调用的地方时,该函数的参数会自动指定类型,最好不要加类型注解

const names = ['abc', 'cba', 'nba']
// 根据上下文环境推导出来的,这个时候可以不添加类型注解
// 上下文中的函数,可以不添加类型注解
names.forEach(function (item,index,arr) {
  console.log(item,index,arr)
})

这是因为TypeScript会根据forEach函数的类型以及数组的类型推断出item的类型;
这个过程称之为上下文类型(contextual typing),因为函数执行的上下文可以帮助确定参数和返回值的类型;

TS类型: any类型 | unknow类型

1.在 TypeScript 中,任何类型都可以被归为 any 类型,因此any 类型也是类型系统的顶级类型。
2.变量如果在声明的时候,未指定其类型,会被识别为任意值类型。
3.unknownany一样,所有类型都可以分配给unknown ,都表示不清楚是类型。

unknown 是 top type (任何类型都可以赋值给unknow类型) , 而 any 既是 top type, 又是 bottom type (任何类型都可赋值给any类型的变量,any类型可以赋值给任何类型) 。

区别赋值描述
anyany上访问任何属性都是允许的,也允许调用任何方法any 类型可以赋值给任意类型any相当于关闭类型检验
unknow默认情况下unknow类型上不能访问属性和调用方法(执行任何操作?),必须进行类型校验(缩小)才可以执行对应操作unknow 类型只能赋值给 any 和 unknow 类型unknow是更加安全的any,会进行类型检测
let foo:unknown = 'aaa'//合法
foo = 123 //合法
console.log(foo);//合法
console.log(foo.length); //非法
//类型缩小
if(typeof foo ==="string"){
console.log(foo.length); //合法
}

TS类型: void类型

当函数没有返回值,则返回void类型

function foo (num1:number,num2:number):void{
    console.log(num1+num2)
    return undefined
    //null也可以
    return null
}
/*
foo的类型是()=>void 表示是一个返回void的函数类型
const类型推导,推导出来是字面量类型
const height = 1.88 //1.88为heigth的类型,1.88是字面量类型
const height:1.88 //等价于
*/
const foo = () =>{}
//等价于
type FooType = () => void
const foo:FooType = ()=>{}

说明
1.可以返回null和undefined,因为默认情况下nullundefined是所有类型的子类型。
2.方法没有返回值将得到undefined,但是需要定义成void类型,而不是undefined类型,否则将报错。
3.当基于上下文的类型推导推导出返回类型为void的时候,并不会强制函数一定不会返回内容。(了解)
在这里插入图片描述

let names:Array<string> = ['arr1','arr2']
names.forEach((item,index,arr)=>{
    return 123;//没有意义但是不会报错
})

TS类型:never类型 (几乎不用)

开发中很少使用never类型
never表示永不存在的值
①函数抛出异常,函数不会执行完毕并返回
②函数中执行无限循环的代码,使得程序永远无法运行到函数返回值那一步,永不存在返回。

新版本已经修改,类型推导出来是void

function foo(){
while (true) {}
}
function bar(){
  throw new Error()
}

never的使用场景

//封装工具/框架
function handleMessage(message: string | number ) {
  switch (typeof message) {
    case 'string':
      console.log('string处理方式处理message')
      break
    case 'number':
      console.log('number处理方式处理message')
      break
    default:
      const check: never = message //这一步永远来不到
  }
}

handleMessage('abc')
handleMessage(123)
//另外一个人调用这个函数传入布尔类型,函数定义参数部分添加boolean类型,check:never位置报错,就会发现case少了一个布尔类型

TS类型:Tuple(元组)

元组类型表示一个已知元素数量和类型的数组(固定长度和类型的数组),各元素的类型不必相同。

数组中通常建议存放相同类型的元素,不同类型的元素是不推荐放在数组中。如果不同类型放在数组中,取值时并不清楚每个元素的具体类型。

使用元组类型的好处
1.元组数据结构中可以存放不同的数据类型,取出来的item也有明确的类型
2.减少对象key的使用

let x: [string, number] = ['hello', 10];
console.log(x[0])

//类型推导:(string|numbner)[] ,取值并不清楚每一个元素是什么类型
let y = ['hello',10]

使用场景:当函数需要返回多个值的时候

function useState<T>(state: T) {
  // T 泛型
  let currentState = state
  const changeState = (newState: T) => {
    currentState = newState
  }
  const tupple: [T, (newState: T) => void] = [currentState, changeState]
  return tupple //返回值为[参数,函数]
}

const [
  counter, // number
  setCounter // (newState: number) => void
] = useState(10)

setCounter(1000)

const [
  title, // string
  setTitle //  (newState: string) => void
] = useState('abc')

const [
  flag, // boolean
  setFlag //  (newState: boolean) => void
] = useState(true)

语法细节

1.联合类型 | 和交叉类型 &

联合类型:取值可以为多种类型中的一种,使用 | 分隔每个类型
交叉类型: 一般用于多种对象类型取交集 ,使用& 分隔

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven'; // OK
myFavoriteNumber = 7; // OK

说明
1.联合类型中的每一个类型被称之为联合成员(union’s members)
2.通常配合缩小联合一起使用

function printId(id:number|string){
	if(typeof id === 'string'){
	//缩小范围
	}else{
	}
}

交叉类型使用场景

interface Person {
	name:string
	age:number
}
interface Fans {
	name:string
    like:string
}
const Women :Person&Fans = {
    name:"ranran",
    age:18,
    like:'study'
}

2.type和interface接口作为类型声明

type 用于定义类型别名 (type alias)

//使用type声明对象
type IDType = string | number | boolean
//分号和逗号可以
type PointType = {x: number,y: number}
type PointType = {x: number;y: number}

type PointType = {//换行可以省略分号,因为js中换行会自动添加分号
  x: number
  y: number
}

接口可以看成规则,实现接口就必须满足接口设置的规则
语法 interface 接口名

//使用借口来声明对象
interface PointType{
    x:number
    y:number
}
interface PointType{
	z:number
}
//多次声明需要都满足
const obj:myInterface = {//obj对象必须满足接口的规则有name和age
	x:1,
	y:2,
	z:3
}

类型别名与接口的区别

-相同点不同点
别名1.类型别名和接口非常相似,在定义对象类型时,大部分时候可以任意选择使用。
2.接口中几乎所有的特性都可以在type中使用
1.使用范围更广
2.不允许同名声明两次(报错)
接口1.只能声明对象
2.可以多次声明同一个接口名称,属性叠加

开发常用:其他数据类型常用type,对象常用interface因为可扩展性更强

接口的继承extends

接口支持多继承

interface Person {
	name:string
	age:number
}
interface Fans extends Person{
    like:string
}

3.类型断言 as 和非空类型断言

类型断言:TypeScript 无法获取具体的类型信息,但是开发员自己清楚,就可以显式告诉TS
非空类型断言: !表示在此处告诉编译器,不会为nullundefined

// message -> undefined | string
function pringMessage(message?: string) {
  console.log(message!.length) //显式告诉不为空,如果不这样写会报错,因为message可能为空
}

pringMessage('Hello World')
pringMessage()

4.字面量类型和类型缩小

可以联合多个类型,类似于枚举的作用

//left right center分别是一个字面量
type Alignment = 'left' | 'right' | 'center'
let align:Alignment = //从上述字面量中选择一个

字面量本身就是string类型,所以对象在进行推理时会把options里的属性推理为string类型

type Method = 'GET' | 'POST'
type requestInfo= {
  url: string
  method: Method
}

//其他细节
function request(url: string, method: Method) {}
//报错的情况
const options= {
	url: 'http://www.coderwhy.org/abc',
	method: 'POST' // 类型推导,推导出来 options.method 是 string 类型
 }
request(options.url, options.method) // 第二个参数报错
options.method = '123' // 存在安全隐患 
//解决方案1,优选
const options: requestInfo= {
	url: 'http://www.coderwhy.org/abc',
	method: 'POST'
}
//解决方案2:进行类型断言(但是不方便别人维护时阅读)
request(options.url, options.method as "post") 
//解决方案3:设置options的时候就确定类型
const options:{url:string,methond:'POST'}= {
	url: 'http://www.coderwhy.org/abc',
	method: 'POST' 
 }

//解决方案4:tips写法
const options = {
  url: 'http://www.coderwhy.org/abc',
  method: 'POST'
} as const //把属性变成字面量,仅读

request(options.url, options.method)

类型缩小: 变量有一个确定的类型,在使用时对其类型做更小的类型限制,使得代码更加安全

  • typeof
  • 平等缩小(一些等号相关) instanceof
  • in(用于确定对象是否具有带名称的属性,如果指定的属性在指定的对象或其原型链中,则in 运算符返回true)
// 1. typeof 的类型缩小
type IDType = number | string
function printID(id: IDType) {
  if (typeof id === 'string') {
    console.log(id.toUpperCase())
  } else {
    console.log(id)
  }
}

// 2. 平等的类型缩小 (=== == !== !=/switch)
type Direction = 'left' | 'right' | 'top' | 'bottom'
function printDirection(direction: Direction) {
  //下面这串代码可以省略了
  //1. if 判断
  // if (direction === 'left') {
  //   console.log(direction)
  // } else if() {}
  // 2. switch 判断
  // switch(direction) {
  //   case 'left':
  //     console.log(direction);
  //     break
  //   case ...
  // }
}

// 3. instanceof
function printTime(time: string | Date) {
  if (time instanceof Date) {
    console.log(time.toUTCString())
  } else {
    console.log(time)
  }
}

class Student {
  studying() {}
}

class Teacher {
  teaching() {}
}

function work(p: Student | Teacher) {
  if (p instanceof Student) {
    p.studying()
  } else {
    p.teaching()
  }
}

// 4. in
interface Fish  {
  swimming: () => void
}
interface Dog  {
  running: () => void
}

function walk(animal: Fish | Dog) {
  if ('swimming' in animal) { //不能用.运算符时可以考虑in
    animal.swimming()
  } else {
    animal.running()
  }
}

const fish:Fish  = {
  swimming() {
    console.log('swimming')
  }
} 

5.函数的类型和函数签名

函数表达式

编写函数类型的表达式(Function Type Expressions),来表示函数类型

函数类型表达式:(参数列表) => 函数返回值类型

function calc(
  n1: number,
  n2: number,
  fn: (num1: number, num2: number) => number
) {
  return fn(n1, n2)
}


const result1 = calc(20, 30, function (a1, a2) {
  return a1 + a2
})
console.log(result1) // 50

//这里虽然只传入一个参数,但是不会报错
const result2 = calc(20, 30, function (a1) {
  return a1 //因为只使用了一个参数,所以可以只传入一个参数
})

console.log(result2) // 600

对于传入为函数类型的参数,当实参小于形参时不会报错会自动省略多余形参
可以想象匿名参数forEach,使用时并不是所有形参都传进去了

函数调用签名:支持声明属性

函数本身可以调用,并且函数也是一个对象,是对象就有属性。

格式:(参数列表):返回值类型 用于对象内
作用:描述带谁能够的函数,可以在一个对象类型中写一个签名,签名的作用是表示这是一个函数可以被调用

interface Bar{
  name:string
}
//不会报错
const bar:Bar =(num1:number):number =>{
    return 123
}
//调用时报错此表达式不可调用,因为bar的类型是Bar,并不知道其是一个函数可以被调用
bar()

//函数调用签名
interface Bar{
  name:string
  (num1:number):number
}
const bar:Bar =(num1:number):number =>{
    return 123
}
bar(123)

如果仅描述一个函数可以被调用,使用函数表达式。如果在描述函数作为对象可以被调用,同时也有其他属性时,使用函数调用签名。

构造签名(理解)

JavaScript函数也可以使用new操作符调用,当被调用时,TS会认为这是一个构造函数,因为产生了一个新的对象。

function foo(){
}
//可以new 但是不知道f的类型,会推断f为any
const f = new foo()

//问题:如何表达fn是一个构造函数,不是一个普通函数
function factory(fn){
  return new fn()
}
//解决:使用构造函数签名
class Person{ 
}
interface ICTORPerson{//符合当前规则的函数就是构造函数,可以通过new
  new():Person //new调用后的返回值是Person的示例,相当于这个函数new了之后返回Person示例
}
function factory(fn:ICTORPerson){
  const f = new fn()
  return f
}
factory(Person)

6.函数的重载和this类型

函数的重载(通用库和工具中使用较多)

函数的重载:函数的名称相同,但是参数不同的几个函数,就是函数的重载

实现需求: 如果我们编写了一个add函数,希望可以对字符串和数字类型进行相加
解决办法: 编写不同的重载签名(overload signatures)来表示函数可以以不同的方式进行调用

  //需求:如果我们编写了一个add函数,希望可以对字符串和数字类型进行相加
  //错误写法
  type AddType = number | string
  function add(a1: AddType, a2: AddType) {
     return a1 + a2  //报错AddType和AddType类型不能相加
   }
  /*写法1联合类型的缺点
  1.进行很多逻辑判断
  2.返回值的类型不能确定
  */
  function add(a1: number | string, a2: number | string) {
    if (typeof a1 === 'number' && typeof a2 === 'number') {
      return a1 + a2
    } else if (typeof a1 === 'string' && typeof a2 === 'string') {
      return a1 + a2
    }
  }

实现方法:一般是编写两个或者以上的重载签名,再去编写一个通用的函数以及实现
调用时根据传入的参数类型决定执行函数体时,执行哪一个函数的重载签名;

注意点:

  1. 重载签名没有函数体卸载通用函数之前
  2. 通用函数不能被调用
//1.2一起构成函数重载
//1.重载签名,不需要函数体
function add(num1: number, num2: number): number
function add(num1: string, num2: string): string
//2.通用的函数实现 ,通用函数不能被直接调用
function add(a1: any, a2: any) :any{
  return a1 + a2 
}

联合类型和函数重载都可以实现一个功能时,优先选择联合类型,联合类型不能实现时再选择函数重载

this类型(很少使用)

没有对js进行特殊配置时,this的默认类型是any

  const info = {
    name: 'why',
    eating() {
      console.log(this.name + ' eating')
    }
  }
  info.eating()
  function foo(){
    console.log(this)
  }
  foo()

tsc --init 初始化配置文件tsconfig.json

当这个参数为true时,TypeScript会根据上下文推导this,但是不能正确选择时,就会报错,需要明确指定this的类型。
指定规则: 函数的第一个参数,参数名必须是this,其他参数从第二个开始传递,编译成js代码是第一个参数会被删除。

//this配置:没有模糊不明确(比如any,所以配置之后上述代码会报错)的this
noImplicitThis:true

//显式指定this的类型,将this作为函数的第一个参数,名字必须是this
type ThisType = { name: string }
function eating(this: ThisType, message: string) {
  console.log(this.name + ' eating', message)
}
const info = {
  name: 'why',
  eating
}
// 显式绑定
eating.call({ name: 'kobe' }, '呵呵呵')
eating.apply({ name: 'james' }, '嘿嘿嘿 ')

this相关的内置工具(了解)

TypeScript提供一些工具类来辅助进行常见的类型转换,这些类可以全局使用。

  • ThisParameterType<>:用于提取一个函数类型Type的this参数类型,没有this参数返回unknown
  • OmitThisParameter<>:移除Type的this参数类型,并返回当前的函数类型
  • ThisType<>:用于绑定上下文的this类型(piana里的getters和action,讲的很奇怪,先记下来)
type ThisType = { name: string }
  function eating(this: ThisType, message: string) {
    console.log(this.name + ' eating', message)
  }

//获取函数eating的类型:type ty = (this: ThisType, message: string) => void
type ty =  typeof eating
//提取出函数里面this的类型type eatingThisType = {name: string;}
type eatingThisType = ThisParameterType<ty>
//type pureEatingType = (message: string) => void
type pureEatingType = OmitThisParameter<ty>

interface IState{
    name:string,
    age:number
}  
interface IStore{
  state:IState,
  eatting?:()=>void,
}
//给store绑定上下文this到IState
const store:IStore & ThisType<IState>  = {
  state:{
    name:"ranran",
    age:18
  },
  eatting:function(){
    console.log(this.name)//如果没绑定获取到的应该是store的this里面没有name属性,需要使用this.state.name获取。当很多函数使用到this.state里面的属性时代码会很冗余,将store的this绑定到IState之后,this就从state里面取name。
  }
}

7.函数的参数

  • 必传参数 -> 有默认值的参数 -> 可选参数

可选参数

使用指定某个参数是可选的,类似于可选属性

//可选参数的类型:number | undefined
//y -> undefined | number
function foo(x: number, y?: number) {
    console.log(x, y)
}
foo(20, 30)
foo(20)
默认参数
  • 有默认值的情况下,类型注解可以省略
  • 有默认值的参数是可以接受undefined类型,不写时相当于传入了undefined,当ts发现接受到undefined就给参数赋默认值(了解即可,使用时认为是指定的,不是联合的)
// 必传参数 -> 有默认值的参数 -> 可选参数
function foo(x: number, y: number = 100) {
  console.log(x, y)
}
//省略类型注解的情况
function foo(x: number, y = 100) {
  console.log(x, y)
}

foo(20) // 20 100
剩余参数

剩余的参数会被封装成一个数组

function sum(initalNum: number, ...nums: number[]) {
  let total = initalNum
  for (const num of nums) {
    total += num
  }
  return total
}

console.log(sum(20, 30))
console.log(sum(20, 30, 40))
console.log(sum(20, 30, 40, 50))

TypeScript 类的使用

类的特性

ES6的class可以看作只是一个语法糖,看成构造函数的另一种写法,所以类可以看成是一个有构造签名的函数。

//案例1
class Point{}
typeof Point //'function'
Point === Poin.prototype.constructor //true
//案例2 类可以看成是一个有构造签名的函数
class Person{};
//表示stor是一个函数类型,并且是一个构造函数
function factory(stor: new()=> void){}
factory(Person)
//案例2的写法2
class Person{};
interface ty{
    new():void
}
//表示有构造签名的
function factory(stor: ty){}
factory(Person)
  • static静态属性(方法)/类属性(方法):不需要创建实例就可以使用,是属于类的通过类名.属性(方法)调用
  • 实例属性(方法):是属于实例化出的这个对象,通过对象.属性(方法)调用
  • 类的所有方法都定义在类的prototype属性上
  • 实例属性除非显式定义在本身(this),其余都是定义在原型上

1.成员属性必须先声明

class Person{
  //1.成员属性必须要先声明
  name:string = ""; //可以类型注解和设置初始化值
  age:number
  constructor(name:string,age:number){
    this.name = name
    this.age = age
  }
}
const p1 = new Person("ranran",18)
const p2 = new Person("biubiu",18)

2. 类的成员修饰符

成员修饰符描述范围
public(默认值)属性为共有属性任意位置访问和修改
protected属性为保护属性此类和其子类中(子类实例也不可以)访问和修改
private属性为私有属性只能在此类里访问和修改

3.可见修饰符的构造器语法糖(参数属性)
TypeScript提供特殊的语法参数属性: 可以将构造函数参数转换成一个同名同值的类属性

1.创建声明同名的成员属性
2.可见修饰符修饰成员属性(权限 | readonly)

//完整写法
class Person{
	public name:string; 
	constructor(name:string){
		this.name = name;
	}
}
//简洁写法
class Person{
	constructor( public name:string){
		this.name = name;
	}
}

4.在TypeScript可以使用存取器使私有属性被外界访问

  • get 属性名(){} 表示getter方法,在属性被读取时自动调用
  • set 属性名(){} 表示setter方法,在属性被修改时自动调用
class Person{
	private _name:string; //私有属性:属性前面使用_(习惯约定)如果没有写下划线,在读取/修改该属性时并不知道访问的是属性name还是方法name
	constructor(name:string){
		this._name = name;
	}
	get name(){
		return this._name
	}
	set name(value){
		this._name = value;
	}
}
const per = new Person("ranan");
console.log(per.name);//读取属性自动调用get name(){}
per.name = "biubiu"; //修改属性set name(){}

5.成员修饰符readOnly只读
readonly属性:表示该属性只读不可以修改,和static连用需要放在其后面

class Person{
	name:"ranan" //定义实例属性
	readonly age:15 //定义只读属性
	call(){} //定义实例方法
}

6.TS类型检测:鸭子类型
TS类型检测:鸭子类型(走得像鸭子声音像鸭子就是鸭子)
鸭子类型:只关心属性和行为是否一样,并不关心是不是具体对应的类型

//情况1:不会被报错
makeArea({getArea:()=>{}})
//情况2
class A {
  name:"ranran"
}
class B{
   name:"ranran"
}
const a:A = new B();

抽象类abstract

抽象类:多个方法有共同方法,但是实现方式有差别。可以将同名方法抽离到父类,由子类去实现抽象方法

  • abstract修饰类 - 抽象类
    • 抽象类不能被实例化,只能被继承
    • 抽象类中可以有抽象方法,也可以有普通方法
  • abstract修饰方法 - 抽象方法
    • 抽象方法只能被定义在抽象类中,子类必须重写抽象类
    • 抽象方法没有方法体
//多态的体现:父类的引用指向子类
function makeArea(shape: Shape) {
  return shape.getArea()
}

abstract class Shape { //抽象类
  abstract getArea() //抽象方法
}

class Rectangle extends Shape {
  private width: number
  private height: number
  constructor(width: number, height: number) {
    super()
    this.width = width
    this.height = height
  }

  getArea() {
    return this.width * this.height
  }
}

class Circle extends Shape {
  private r: number
  constructor(r: number) {
    super()
    this.r = r
  }

  getArea() {
    return this.r * this.r * 3.14
  }
}

const rectangle = new Rectangle(20, 30)
//此时makeArea方法的形参是Shape的子类
console.log(makeArea(rectangle))

const circle = new Circle(10)
console.log(makeArea(circle))

TypeScript对象类型

  • ?可选属性
  • readonly 只读属性
  class Person{
    name?:"ranran"
    readonly age:15
  }

索引签名

索引签名:可以通过索引访问[标识符自己起:通过什么索引访问]:索引访问之后返回值的类型
使用场景:不知道具体的实现是什么样的,但是知道具备某种特性

说明

  • 一个索引签名属性的类型必须是string或者number其中一个
  • 数字类型索引的类型必须是字符串类型索引的子类型

原因:所有数字类型访问索引时最终都是转换为string类型访问的,如果同时设置了,数字类型0拿到的类型需要符合字符串类型’0’拿到的类型

//案例1
interface IndexLanguage {
	[index:number]:string //通过number索引访问,访问之后返回string类型数据
	//这里定义的属性也得满足索引签名的要求
	//如果需要两种类型,需要分开写(语法不支持)
	 [index:string]:string 
}
function getInfo():IndexLanguage {} //不知道具体的实现是什么样的,但是知道具备某种特性
const frontLanguage = getInfo()

//案例2
interface IPerson {
  //告知通过索引获取到的数据类型是什么样
  [index:string]: string
}
//报错原因 ["biu","ranran"]通过字面量创建的Array实例 =>数组中自带了很多其他属性比如name.forEach返回值是Function不是string
const name:IPerson = ["biu","ranran"]

函数签名

interface来定义对象中普通的属性和方法的,实际上它也可以用来定义函数类型(除非特别的情况,推荐使用类型别名来定义函数)

// type CalcFn = (n1: number, n2: number) => number

// 可调用接口
interface CalcFn {
  (n1: number, n2: number): number
}

function calc(num1: number, num2: number, calcFunc: CalcFn) {
  return calcFunc(num1, num2)
}

const add: CalcFn = (num1, num2) => num1 + num2
calc(20, 30, add)

TypeScript接口

类实现接口 implements

接口可以看成规则,对类进行约束

说明

  • 接口只定义对象的结构,而不考虑实际值
  • 在接口中所有的方法都是抽象方法

定义类时可以使类去实现一个接口(满足接口定义的规则),一个子类只能有一个父类,但是可以实现多个接口

interface myInterface{ //接口定义必须有name和sayHello
	name:string,
	sayHello():void; 
}
class MyClass implements myInterface{
	name:stringconstructor(name:string){
		this.name = name;
	}
	sayHello(){} //类的实现
}

抽象类和接口的区别

区别抽象类接口
继承抽象类是类,一个类只能继承一个抽象类一个类可以实现多个接口
方法可以是普通方法和抽象方法全是抽象方法
子类子类必须覆盖抽象类的抽象方法子类必须遵守接口定义的规则,接口中的所有东西都得有

抽象类是对类本质的抽象,表达的是 is a 的关系。比如:male is a Human。抽象类包含并实现子类的通用特性
接口是对行为的抽象,表达的是 like a 的关系。比如:Baoma like a plane(它有飞的功能一样可以飞),但其本质上 is a Car。接口的核心是定义行为,即实现类可以做什么

特殊:严格字面量赋值检测

第一次创建的对象字面量被表示为新鲜的,对于新鲜的字面量会进行严格的类型检测(必须完全满足类型的要求,不能有多余的属性)。
当一个新的对象字面量分配给一个变量或传递给一个非空目标类型的参数时,对象字面量指定不存在的属性是错误的。

name: string
   age: number
}
  
const p: IPerson = { //第一次创建新鲜的,被严格的检测
    name: 'why',
    age: 18,
    height: 1.88//报错,Iperson接口没有height
}
  
const info = { //第一次创建新鲜的,被严格的检测
    name: 'why',
    age: 18,
    height: 1.88 
}
// freshness 不是新鲜的不严格检测
const p2: IPerson = info // 不报错  

TypeScript枚举类型

格式:enum 枚举类型标识符 {}

enum Color {//没设置值,初始值默认从0开始按顺序自增,可以理解为数组下标
  RED,
  PINK,
  BLUE="blue",//设置初始值
}
const pink: Color = Color.PINK;
console.log(pink); // 1

泛型

泛型语法的基本使用

泛型指是在定义函数或者类时并不知道类型,在被使用时才知道数据的类型

泛型可以指定多个,定义泛型<格式类>
T被称为类型变量(type variable),T作为参数泛化成具体类型的作用叫做类型参数化。

  • 泛型也可以指定默认值
//定义泛型T,定义的位置一般在标识符的后面
function fn <T> (a:T):T{
	return a;
}
fn(10); //此时T是number,利用类型推断 const
fn <string>("hello") //显式指定T是string,为了方便阅读,最好显式指定

interface Inter{//定义接口
	length:number;
} 
function fn<T = string>(a:T):number{//这里的泛型必须继承Inter接口
	return a.length;
} 

泛型类的使用案例

class Point<T> {
  x: T
  y: T
  z: T
  constructor(x: T, y: T, z: T) {
    this.x = x
    this.y = y
    this.z = z
  }
}

const p1 = new Point('1.33.2', '2.22.3', '4.22.1')
const p2 = new Point<string>('1.33.2', '2.22.3', '4.22.1')
const p3: Point<string> = new Point('1.33.2', '2.22.3', '4.22.1')

const names1: string[] = ['abc', 'cba', 'nba']
const names2: Array<string> = ['abc', 'cba', 'nba'] 

泛型约束和类型条件

对泛型的类型进行约束

interface Inter{//定义接口
	length:number;
} 
//写法1:这里的泛型继承Inter接口
//T相当于是一个变量,用于记录本次调用的类型,所以在函数执行周期中,会保留参数的类型
function fn<T extends Inter>(a:T):number{
	return a;
} 
const f = fn("aaaa") //写法1便于TS推断f的类型为字符串
//写法2:
function fn(a:Inter):Inter{//这里的泛型必须继承Inter接口
	return a; 
} 
const f = fn("aaaa") //写法2会导致形参a的原始类型丢失,推导f的类型为Inter

keyof 和 in

  • keyof 类型 接受一个对象类型作为参数,返回该对象属性名组成的字面量联合类型
  • in的右侧一般会跟一个联合类型,使用in操作符可以对该联合类型进行迭代。 其作用类似JS中的for...in或者for...of
//keyof使用案例
type Dog = { name: string; age: number;  };
type D = keyof Dog; //type D = "name" | "age"

//in的使用案例
type Animals = 'pig' | 'cat' | 'dog'​
type animals = {
    [key in Animals]: string
}
// type animals = {
//     pig: string; //第一次迭代
//     cat: string; //第二次迭代
//     dog: string; //第三次迭代
// }

keyof any返回联合类型string | number | symbol

补充:遇见索引签名时,keyof会直接返回索引的类型。索引类型为string时,keyof返回的类型是string | number,这是因为数字类型索引最终访问时也会被转换为字符串索引类型。

  type Dog = {  [y:number]: number  };
  type dog = keyof Dog;  //type dog = number
  ​
  type Doggy = {  [y:string]: boolean };
  type doggy = keyof Doggy; //type doggy = string | number

使用场景:与extends关键字结合使用,对对象的属性做限定。

在泛型中使用的案例

// k extends  "name"| "age" 这里的extends表示k是不是其中一个联合类型
function getObjectProperty<O,K extends keyof O>(obj:O,key:K){
    return obj[key]
} 
const info = {
    name:"ranran",
    age:18
}

getObjectProperty(info,"name")

TypeScript映射类型(开发中很少用,用于封装组件)

映射类型:可以理解为将一个类型映射成一个新的类型

使用场景:一个类型需要另一个类型,但是又不想拷贝(映射)一份,可以考虑使用映射类型。

可以将映射类型想象成一个函数,函数的作用就是拷贝(映射)类型

//映射类型MapPerson
//<>定义泛型,接收需要拷贝的类型  
type MapPerson<Type> = {
    //[index:number]:any //索引签名写法
    //Property自定义的标识符, keyof Type表示联合类型,in从联合类型中依次取值赋值给Property
    [Property in keyof Type]:Type[Property]
}
interface Iperson{
    name:string
    age:number
}

type NewPerson = MapPerson<Iperson> //使用映射类型拷贝Iperson类型

映射类型修饰符

可能使用的两个类型

  • readonliy,设置属性只读
  • 设置属性可选

可以通过前缀-或者+删除或添加这些修饰符,默认使用+

//映射类型MapPerson
//将所有属性映射为可选属性
type MapPerson<Type> = {
   //+readonly [Property in keyof Type]:Type[Property]
   -readonly [Property in keyof Type] -?:Type[Property]
}

interface Iperson{
    name:string
    age:number
}

type NewPerson = MapPerson<Iperson>
/*
type NewPerson = {
    readonly name: string;
    readonly age: number;
}
*/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据库应用:CentOS 7离线安装MySQL与Nginx

目录 一、理论 1.安装依赖 二、实验 1.离线安装MySQL与Nginx 2.离线安装Nginx 三、问题 1.执行nginx -v命令报错 四、总结 一、理论 1.安装依赖 &#xff08;1&#xff09;概念 安装依赖是指在软件开发中&#xff0c;为了运行或者编译一个程序或者库&#xff0c;在计…

JVM——类加载和垃圾回收

目录 前言 JVM简介 JVM内存区域划分 JVM的类加载机制 1.加载 双亲委派模型 2.验证 验证选项 3.准备 4.解析 5.初始化 触发类加载 JVM的垃圾回收策略 GC 一&#xff1a;找 谁是垃圾 1.引用计数 2.可达性分析 &#xff08;这个方案是Java采取的方案&#x…

k210学习篇(一)环境搭建

一、为什么选择Canmv开发板? 便宜!便宜!便宜!淘宝200即可买到一个能带摄像头和LCD屏等等的开发板 二、利用Maix Hub在线训练 Maix Hub官网https://maixhub.com/home Maix Hub使用教程:K210学习笔记——MaixHub在线训练模型(新版) 注意: 三、配置开发环境 1.MaixPy IDE…

某网站提交登陆信息加密JS逆向实战分析

1. 写在前面 对于爬虫开发者来说&#xff0c;职业生涯中可能或多或少会遇到各种各样的网站&#xff0c;其中有些必要要求登陆才能浏览。那么模拟登陆的时候发现提交的登陆信息&#xff08;用户名、密码&#xff09;都是经过加密后的&#xff0c;如何处理&#xff1f;这里找到了…

我只改五行代码,接口性能提升了 10 倍!

背景 某公司的一个 ToB 系统&#xff0c;因为客户使用的也不多&#xff0c;没啥并发要求&#xff0c;就一直没有经过压测。这两天来了一个“大客户”&#xff0c;对并发量提出了要求&#xff1a;核心接口与几个重点使用场景单节点吞吐量要满足最低500/s的要求。 当时一想&am…

C++ IO流

文章目录 C语言的输入与输出流是什么?CIO流C标准IO流C文件流 stringstream的简单介绍 C语言的输入与输出 在C语言中,我们使用最频繁的输入输出方式为: scanf 和 printf. scanf : 从输入设备(键盘)读取数据,并将值存放在变量中.printf: 将指定的文字/字符串输出到标准输出设备…

数据库约束与表的关系(数据库系列4)

目录 前言&#xff1a; 1.数据库的约束 1.1约束类型 1.1.1 not null 1.1.2 unique 唯一约束 1.1.3 default 默认值约束 1.1.4 primary key 主键约束 1.1.5 foreign key 外键约束 2.表的关系 2.1 一对一 2.2 一对多 2.3 多对一 3.新增 4.聚合查询 4.1聚合函数 4.…

Pinecone - 向量数据库

文章目录 关于 PineconeRoadMapSemantic SearchChatbots购买查看 API Key创建索引代码调用安装库 pinecone-client查看已经创建的索引创建索引插入数据获取索引统计分析信息查询索引,获取相似向量删除索引关于 Pinecone 官网 : https://www.pinecone.i

利用layui构建OA系统基本操作

一.编写方法&#xff08;增加&#xff0c;删除&#xff0c;修改&#xff0c;查询&#xff09; 通过继承BaseDao来实现通用&#xff0c;从而减少代码量&#xff0c;提高小路 1.增加 public int add(User user) throws Exception {String sql "insert into t_oa_user(name…

Windows7中使用SRS集成音视频一对一通话

SRS早就具备了SFU的能力&#xff0c;比如一对一通话、多人通话、直播连麦等等。在沟通中&#xff0c;一对一是常用而且典型的场景&#xff0c; 让我们一起来看看如何用SRS做直播和RTC一体化的一对一通话。 一、启动windows7-docker 二、拉取SRS镜像 执行命令:docker pull oss…

修改jar包中的文件内容

文章目录 导引查找是否存在需要修改的文件vim命令修改配置文件jar命令替换jar包中的文件(也可新增)解压jar包&#xff0c;修改后重新打包jar修改clas文件jar命令参数 导引 首先问问为什么要直接修改jar包中的文件&#xff0c;而不是重新打包&#xff0c;在非必要的情况下&…

【动手学深度学习】pytorch-参数管理

pytorch-参数管理 概述 我们的目标是找到使损失函数最小化的模型参数值。 经过训练后&#xff0c;我们将需要使用这些参数来做出未来的预测。 此外&#xff0c;有时我们希望提取参数&#xff0c;以便在其他环境中复用它们&#xff0c; 将模型保存下来&#xff0c;以便它可以在…

python数据挖掘基础环境安装和使用

文章目录 一&#xff0e;安装python环境二、库的安装2.1 使用pip命令安装virtualenvv扩展&#xff1a;cmd无法使用pip&#xff0c;报错&#xff1a;Fatal error in launcher: Unable to create process using ... 2.2 安装virtualenvwrapper-win2.3 新建一个用于人工智能环境的…

腾讯云 API 3.0(V3版签名) 通用接口 Delphi 版

目录 一、腾讯云API 3.0 简介&#xff1a; 二、Delphi 接口函数说明&#xff1a; 1. Delphi 接口包含的单元&#xff1a; 2. 同步调用和异步调用的区别&#xff1a; 3. 程序调用示例&#xff1a; 三、Delphi 版腾讯云API 3.0 版接口函数下载 四、演示程序录播 下载源程序…

DB2数据库SQL将不同行做合计

DB2数据库SQL将不同行做合计 案例&#xff1a; 将’GL’和’RZ’做合计&#xff0c;其他的不动。 SELECT SALE_TYPE,ROUND(CAST(SUM(aatp_weight) AS DOUBLE),2) AS aatp_weight FROM( SELECT CASE WHEN SALE_TYPE GL THEN RZ ELSE SALE_TYPE END AS SALE_TYPE, DEMAND_NUM / …

基于NXP iMX8MP处理器M7核心LVGL移植

By Toradex胡珊逢 LVGL (Light and Versatile Graphics Library)是一个轻量级的开源图形库&#xff0c;采用 C 或者 MicroPython 语言开发。可以在资源有限的 MCU 上轻松地绘制图形界面。Verdin iMX8M Plus 模块的处理器除了 Cortex-A53 核心外&#xff0c;还具有一个 Cortex-M…

如何运营校园外卖跑腿小程序

运营校园外卖跑腿小程序需要考虑多个方面&#xff0c;包括市场调研、合作伙伴选择、用户获取与留存、服务管理和推广等。下面是一些关键步骤和策略&#xff1a; 市场调研&#xff1a; 在开始运营之前&#xff0c;进行市场调研是非常重要的。了解目标用户的需求和习惯&#xf…

HarmonyOS学习路之方舟开发框架—方舟开发框架(ArkUI)概述

方舟开发框架&#xff08;简称ArkUI&#xff09;为HarmonyOS应用的UI开发提供了完整的基础设施&#xff0c;包括简洁的UI语法、丰富的UI功能&#xff08;组件、布局、动画以及交互事件&#xff09;&#xff0c;以及实时界面预览工具等&#xff0c;可以支持开发者进行可视化界面…

JVM系列(6)——类加载器详解双亲委派

一、类加载器 类加载器是一个负责加载类的对象&#xff0c;用于实现类加载过程中的加载这一步。 主要作用就是加载 Java 类的字节码&#xff08; .class 文件&#xff09;到 JVM 中&#xff08;在内存中生成一个代表该类的 Class 对象&#xff09;。 加载过程可以看 JVM系列&a…

人工智能-反向传播

前面阐述过&#xff0c;在设计好一个神经网络后&#xff0c;参数的数量可能会达到百万级别&#xff0c;利用梯度下降去更新参数计算复杂&#xff0c;算力不足&#xff0c;因此需要一种有效计算梯度的方法&#xff0c;这种方法就是辛顿提出的反向传播&#xff08;简称BP&#xf…