likes
comments
collection
share

Typescript 稍稍入个门

作者站长头像
站长
· 阅读数 40

TypeScript 介绍

TypeScript 是一种由微软开发的自由和开源的编程语言。在对 JavaScript 结构进行静态分析时很有可能是错误,为了防止并排查 JavaScript 运行时的错误,微软创造了一种在编译时进行静态类型分析强类型语言 -- TypeScript。

TypeScript 不仅是 JavaScript 的超集,在兼容 JavaScript 的同时向 TypeScript 中添加了基于类的对象、接口和模块等面向对象特性,并支持跨平台开发,可以在所有主流的操作系统上安装和执行,让代码更具维护性与扩展性,能够帮助我们写出更优雅的代码。

TypeScript 的特性:

  • 编译时类型检查:提供静态类型检查,在编译时能发现潜在的类型错误,避免在运行时出现报错。
  • 面向对象编程:支持面向对象编程,包括类、接口、继承、抽象类等特性。
  • 类型注解:支持类型注解,可以明确地指定变量、函数的类型,提高代码可读性和可维护性。
  • ES6/ES7 的支持:支持 ES6/ES7 的新特性,如箭头函数、解构赋值、async/await 等。
  • 命名空间和模块化:支持命名空间和模块化,可以更好地组织代码。
  • 工具支持:TypeScript 集成了许多开发工具,包括编辑器、调试器、构建工具等,可以提高开发效率。

TypeScript 的安装

在开发环境中安装 Node.js 后我们可以使用 npm 命令安装 typescript

全局安装 TypeScript

npm i -g typescript@4.5.2

// 查看 typescript版本类型
tsc -v

(git bash 是按装了 git 之后才有的)

在项目文件夹中初始化项目

tsc -init

会多一个 TypeScript 配置文件 tsconfig.json

新建 ts 文件 01.ts 编译 ts

tsc 01.ts

其中 tsc 是 TypeScript 编译器的控制台接口,这个命令可以将 ts 编译成 js文件,如果没有以外的话可以在 01.ts 的相同目录下找到一个 01.js 的文件。 Typescript 稍稍入个门 会输出一个新的 js 文件,因为在运行 ts 文件时会被编译成 js文件 可以在 tsconfig.json 中发现编译成 js 的位置 Typescript 稍稍入个门

ts 在定义时 类型已经确定

ts 中 let 和 const的区别: let的类型由值来决定 const的值就是类型

ts 中的基本类型

先来看一下 ts 中是如何申明类型的:

let counter; // 未知为 any 类型
let counter = 0;  // number (number类型被自动推测出来,通过0 能判断出是number类型,该过程被称为类型推导)
let counter:number ;  // number
let counter:number = 1 ;  // number

这些原始类型在 ts 中都有对应的类型注解,可以用来明确指定变量或函数的类型。

变量的类型声明在变量名后的这种风格是基于类型理论,且更强调类型是可选的,在没有声明类型的情况下,ts 会尝试检查赋值给变量的值来推测变量的类型。

当一个变量的类型无法被推导时,一个特殊的类型 any 会作为他的类型。

js 中的原始类型: string、number、boolean、symbol、undefined、null

ts 的原始类型在包含 js 原始类型的基础上多了 void 和用户自定义的 enum类型等基本类型。

所有的类型在 ts 中都是一个唯一的顶层的 Any Type 类型的子类型,any 关键字代表这种类型

let myStr:string = '1'
let bool:boolean = true
let und:undefined = undefined
let myNum:number = 2
let sy:symbol = Symbol('123')
let nul:null = null
let vo:void = undefined  // 函数

void

void 表示没有任何返回值的函数的返回类型,或者变量的类型。


function a(){}  // function a():void 在ts 中函数没有返回值,函数的类型就是 void 

function b():void {}
function c():undefined{return undefined}

enum

枚举类型是为了给一个数组集合更友好的命名,枚举类型可以更加清晰地表达代码的意图,可以用于定义一组有名字的常量,使代码更具可读性和可维护性。

// 枚举类型,用于定义一组有名字的常量
enum Color {Red, Green, Black}
let color:Color = Color.Green  // 1
// 枚举数值默认从0开始依次递增 	 	

默认情况下,从 0 开始为元素编号。 你也可以手动的指定成员的数值。 例如,我们将 http 的请求状态设置为对应的 code 值:success 设置为 200,error 设为 400,serveError 则为 500。

export {}
//枚举  不是用来定义类型,用于列举数据

enum status{
  success = 200,
  error = 400,
  serveError = 500
}

const code:string|number = 400
if(code === status.success){
  console.log('success')
} else if(code === status.error) {
  console.log('error')
} else if(code === status.serveError){
  console.log('serveError')
}

或者,全部都采用手动赋值:

// 若是枚举中没有赋值 则默认从 0 开始递增赋值
enum statusCode{
  success,   // 0
  error = 400,    // 400
  serveError // 401
}

ts中的非原始类型

在 ts 中,非原始类型指的是除了原始类型(number、string、boolean、null、undefined、symbol)之外的其他类型

对象类型

Object 定义了一个只能保存对象的变量,是一个非原始类型。它是所有对象的基类。它包含了一些常用的属性和方法,也可以使用接口(interface)来定义。

export {}  //模块化,防止当前文件的变量全局化,实现文件之间的数据隔离
//  ts 中的非原始类型 : object、  Object、  {}

// object 不包含基础类型,用于区分类型
let obj:object = {a:1}
let arr:object = [1,2]

// let bool:object = true //报错
// let num:object = 11 //报错


// Object 包含基础数据类型
let obj1:Object = {a:1}
let arr1:Object = [1,2]
let bool:Object = true 
let num1:Object = 1 


// {} 等效于Object,包含基础数据类型 
let obj2:{} = {a:1}
let arr2:{} = [1,2]
let boo2:{} = true 
let num2:{} = 1 

数组

在 ts 中可以 像 js 一样可以操作数组元素。 有两种方式可以定义数组:

  1. 可以在元素类型后面接上[],表示由此类型元素组成的一个数组
  2. 使用数组泛型,Array<元素类型>:
// 数组中元素的数据类型都一致便于后续数据的处理
// 数组的第一种写法:直接拼接 []
let arr:number[] = [1,2]
arr[0]=10
// arr[1]='123'

// 第二种写法: 泛型 类型参数化 (将数据类型作为参数)
let arr2:Array<number>= [1,2,3]                                                        

元组

元组类型( Tuple )允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 string 和 number 类型的元组。

// 元组 其中的数据不一定是相同类型,每一项的数据类型被定死
let arr3:[number,number,boolean] = [1,2,true]

联合类型(|)

所谓的联合类型指的是一个值可以是多种类型中的一种,可以用联合类型(T1 | T2 | ...)来定义。 其中或操作符 | 只要满足一个条件即可,或者应用在所有条件都满足的情况下。

// | 联合类型 也可以理解为 或者

let numberStr:number|string = 10
numberStr = 'abc'


let num: 1|'2'=1  // 1  和 '2' 是类型  常量,表示num 的值只能是 1 或 '2'


let obj:{a:'1'}|{b:'3'}   // 声明类型

obj = { a:'1' }
obj = { b:'3' } 
obj = { a:'1',b:'3' }   // obj 中的 | 表示要么有 a 属性 要么有 b 属性  或者都有

// obj = {a:'1',b:'3', c:1}    // c 属性 报错

交叉类型 (&)

在表示多个类型的结合时可以使用交叉类型,&操作符要求所有的条件必须同时满足

//  & 交叉类型 同时满足两个条件

let a:number&string   // 不会有任何值能够在满足 number 的情况下又是一个 strinmg

let obj:{name:string,age:number} & {height:number}  // & 必须都得满足

obj={name: 'Gina',age:18,height: 168}

any & unknow

接下来认识两个 TypeScript 中的顶级类型,他们都能够用来表示动态类型或未知类型。

  • any: 表示任意类型,可以被赋值为任意类型,也可以赋值给任意类型。使用 any 类型会关闭 TypeScript 的类型检查,因此不建议滥用。
  • unknown: 表示未知类型,它类似于 any,但是比 any 更安全。使用 unknown 类型时,必须进行类型检查或类型断言才能将其赋值给其他类型。这可以避免在运行时出现类型错误。
//  any
let number:any  // any 绕过类型校验
number = '10'
number = 10
number ={b:10}
number.toFixed(2)  // 绕过类型校验  不会在编译时报错


let n:unknown  
n = 1
n = '10'
n= [10]

// n.toFixed(2)   // 报错  会进行类型的校验,   除非确定 变量类型为 number 才不会报错

// 通过 typeof  进行类型的校验,实现了类型守护
if(typeof n ==='number'){
  n.toFixed(2)
} else if( typeof n ==='string'){
  n.concat()
}

any 和 unknown 的区别:

  • any 可以被赋值为任意类型,而 unknown 必须进行类型检查或类型断言才能赋值为其他类型。
  • any 关闭了 TypeScript 的类型检查,而 unknown 更安全,可以避免在运行时出现类型错误。

interface

当我们需要表示一个对象的结构,包括属性、方法、索引等,可以使用接口(interface)来定义。

// interface 用于 自定义类型

//  定义接口类型  --- 对象
interface MyInterface{
  // 属性名: 值的类型
  name: string,
  age: number,
}


// 用接口来校验对象
let me: MyInterface ={
  name:'Gina',
  age: 18
}


// 定义接口类型  --- 数组
interface Arr {
  // [index:number] 下标类型: 值类型
  [index:number]: number|string 
}

let arr = [1,2,3,'4']

// 定义接口类型  --- 函数
interface FnIn{
  //  形参及类型: 返回值类型
  (p:string): void
}

let fn:FnIn = (p:string)=>{
}
fn("")  // 若不传参则会报错

T我们可以将接口理解为是对象的状态(属性)和行为(方法)的抽象(描述)。

继承 同名 缺省

  • 继承:使用 extends 关键字来实现继承。子类可以继承父类的属性和方法,并且可以通过 super 关键字调用父类的构造函数和方法。
  • 同名:如果子类和父类拥有同名的属性或方法,子类会覆盖父类的同名属性或方法。如果需要在子类中调用父类的同名属性或方法,可以使用 super 关键字。
  • 缺省:在 TypeScript 中,可以使用 ? 符号来表示一个属性或方法是可选的。如果一个对象没有某个可选属性或方法,访问它不会产生运行时错误。

 interface NameInter{
  readonly name:string  // readonly 修饰符 表示只读属性, 只允许读取,不支持修改,否则会报错
 }


 interface AgeInter{
  age?:number  // 属性名?  表示这个属性 可以缺省 (定义数据时该属性可有可无)
 }

//  接口继承的格式, extends 特点:  可以继承父接口的所有属性,且可以同时继承自多个不同的接口
 interface PersonInter extends NameInter,AgeInter{
  height:number
 }


//  必须同时拥有 继承下来的属性,少写一个都会报错
 let person:PersonInter
 person = {
  name:'Gina',
  age:18,
  height: 168
 }


//  接口可以同名 特点:会合并同名接口的所有属性
 interface AInter{
  a: number
 }
 interface AInter{
  b: number
 }

//   a、b 属性都得有,少写一个都会报错
 let obj:AInter ={
  a:1,
  b:2
 }

**接口有一个特点就是当有接口同名时会将属性进行拓展:**如果一个接口继承自另一个接口,它会拥有另一个接口的所有属性和方法,并且可以添加自己的属性和方法。

联合交叉类型

在 TypeScript 中,可以使用联合类型和交叉类型来组合多个类型,联合交叉类型即为联合类型和交叉类型的组合使用。

//  && 的优先级大于 || 
console.log( 1||2&&3)  // 1(2&&3) ===>>> 1


//  联合交叉类型  ( | & )
//   &  优先于 |

let obj:{name:string} & {age:number} | {name:number} & {age:string}


// obj  要么完全符合前者  {name:string} & {age:number}  
obj = {name :'Gina',age:18}

//  要么完全符合后者  {name:number} & {age:string}
obj = {name :20,age:'18'}

type 类型(类型别名)

使用 type 关键字来创建类型别名,用于给一个类型起一个新的名字。类型别名可以用于简化复杂类型的定义,提高代码可读性和可维护性。

export{}

// type 自定义类型 (类型别名)

type numOrStr = number|string

let str:numOrStr = 10

str = 'haha'


// 给对象定义类型
type ObjType = {a:number&2,b:string}
// type ObjType = {c:number}  // type 同名会报错

let obj:ObjType={
  a:2,
  b:'123'
}



// type 和 interface 的区别
// 共同点 都可以用来自定义类型
// 区别: interface 可以重复命名, 
//        type 不支持重复定义,支持联合交叉类型定义

ts 函数

在 ts 中可以对函数的类型进行定义,包括参数类型和返回值类型,可以使用函数类型((arg1: T1, arg2: T2, ...) => R)来定义。

// (a:形参类型):return的返回类型
function fn(a:number,b:number):number{
	return a + b
}

  
// 接口定义函数类型
interface FnTnter{
	(p:string):number
}

let  fn1:FnTnter = (p:string)=>{
	return 1
}

fn1('')



// 类型别名定义函数类型
type Fntype = (p:string)=>void

let fn2:Fntype = (p:string):void =>{}

fn2('')



// 函数 作为对象的属性出现

  
// interface 
interface ObjFnInter{
	fn: FnTnter
}

let obj1:ObjFnInter = {
	fn:(string)=>{
    return 1
  }
}
obj1.fn('')

  
  
//  type
type ObjType = { fn:(p:string)=>number}
let obj2:ObjType = {
  fn:(str)=>{
    return 1
  }
}
obj2.fn('2')

ts 函数参数


// 默认参数 参数名:number=3 这个参数的默认值是3
function fn(a:number , b:number=3){
	return a+b
}
console.log(fn(1,2));
console.log(fn(5));
console.log("--------")
// 缺省参数参数名? 表示可以被缺省的参数
function fn1(a:number , b?:number){
	return 1
}
fn1(1,2);
fn1(1);
  
// 剩余参数
function fn2(a:number , b:number, ...arr:number[]){
	console.log(a,b);
  console.log(arr);
}
fn2(1,2,3,5,4)


// let arr1 = [1,2,3];
// let arr2 = [...arr1];
// arr1[0] = 4;
// console.log(arr1);
// console.log(arr2);


  
let obj1 = {a:1,b:2,c:[1,2,3]3}
let obj2 = {...obj1}  //浅拷贝
obj2.a = 3

ts 中的 Promise

在 ts 中,也可以像 js 一样使用 Promise 类型来处理异步操作。

Promise 是一种表示异步操作结果的对象,它可以在异步操作完成时返回一个值或抛出一个异常。

interface DataItff{
	a:number;
	b:number;
}

interface ResItff{
 code:number;
	data: DataItf[]; // 对象数组的形式  {a:number,b:number}[]; 
  message:string;
}


// promise对象: p对象名:Promise<res的类型>
let p:Promise<ResItf> = new Promise((resolve,reject)=>(
  resolve({
		code:0,
		data: [(a:1,b:2),(a:11,b:22)]
		message:''
  })
})


p.then(res=>{
  if(res.code==0){
  	res.data.map(item=>item.a)
  }
})

ts 中的this 指向

在浏览器环境中,this 来源于默认值 window, window 就是 Window 的实例对象, 而函数中的 this 指向可以使用箭头函数或 Function.prototype.bind() 方法来指定。

//  this 指向
// 在全局上给window接口扩展  当 接口同名时会合并属性 进行扩展
interface Window {  
  myName:string
}

// ts 提供了 Window 类型, window 就是Window 类型的对象
function Person(this:Window,name:string){
  // ts 的书写中 需要指明 this 指向,在函数的第一个形参位置注明
  // 当 Window 类型中没有 myName 这个属性时需要自己扩展
  this.myName = name
}

window.Person('haha')

要拓展 Window 属性必须在全局下注册,那如果将该文件设置为私有的作用域时会发生什么呢? Typescript 稍稍入个门 该文件将不再是全局环境,Person() 将不属于window,将 Window 的接口拓展提取到全局的文件下,防止文件间的隔离

interface Window {  
  Person:(n:string)=>void
  myName:string
}

当设置了 export{} 后又会发生什么呢


export {}
//  当设置了 export{} 后 这里就不是全局,在这里扩展的 Window 接口失效,需要写到 global.d.ts 的全局作用域下

// ts 提供了 Window 类型, window 就是Window 类型的对象
function Person(this:Window,name:string){
  // ts 的书写中 需要指明 this 指向,在函数的第一个形参位置注明
  // 当 Window 类型中没有 myName 这个属性时需要自己扩展
  this.myName = name
}

// 将Person 函数注册到 window 的 Person 属性上
// 加了 export{} 后这里就不是全局,Person 不是 window 下的属性
window.Person = Person
window.Person('haha')

this 指向自定义对象

this 关键字引用当前对象。如果需要在自定义对象中使用 this,可以使用箭头函数或者在方法中使用 this。

export{}

type objType={
  name: string,
  Person:(n:string)=>void
}
let obj:objType ={
  name:'haha',
  Person:()=>{}
}

// 定义函数的时候  this 的类型必须和调用函数时的类型保持一致

// this: Window|ObjType  可以给多种类型所对应的对象 让 this 去指向
function Person(this:objType,name:string) {
  this.name = name
}

obj.Person = Person
obj.Person('123')

泛型(接口、类型别名)

泛型 指的是 类型参数化,将原来某种具体的类型进行参数化

//  泛型  将类型作为参数传入,
//  T 是一个标识符 可以自定义,T 表示一种类型
// 类型参数化
function fn<T>(n:T):T{
  return  n
}

fn<number>(100)
fn<string>('123')
//  将值作为类型传入,那么传入的 参数只能是值得本身
fn<'hello'>('hello')



function fn1<T,G>(n:T,m:G):T{
  return  n
}

fn1<string,number>('123',100)
fn1<boolean,number>(true,100)

//  通过泛型声明数组
let arr:Array<number> = [1]

通过 类型别名、接口声明泛型

// 泛型 -- 类型别名
type StrOrNumber = string | number

type ObjType<N,G> = { name: N, getName:()=>G}

let obj:ObjType<StrOrNumber,StrOrNumber> = {
    name: 123,
    getName(){
      return 1
    }
}


// 泛型 -- 接口
// 可以设置默认类型,在使用时可以省略不传
interface ObjInter<T,G = number> {
  name: T,
  getName:()=>G
}

let obj2:ObjInter<StrOrNumber> = {
  name: 'yiyi',
  getName() {
    return 123
  },
}

泛型约束 (extends)

通过泛型约束可以用来限制泛型类型参数的类型范围。泛型约束使用 extends 关键字来指定所允许的类型。

type StrOrNumber = string | number 

//  extends  设置泛型只能接收的类型 
interface ObjInter<N extends StrOrNumber,G>{
    name: N,   //  N 只能接收字符串  或者数字
    getName: ()=>G
}

let obj:ObjInter<number,string> ={
  name:1,
  getName() {
    return '123'
  },
}

[]动态取值

在 Vue 3 和 TypeScript 中,可以通过 [ ] 动态访问属性名。假设有一个对象 obj,可以使用以下代码访问其动态属性:

typescript复制代码const obj = { foo: 'bar' };
const prop = 'foo';

console.log(obj[prop]); // 输出 'bar'

注意:由于 TypeScript 的限制,可能需要将对象类型声明为 { [key: string]: any },以确保可以使用动态属性名。

例如,可以这样声明上面的 obj 对象:

const obj: { [key: string]: any } = { foo: 'bar' };

TypeScript 中的 class 是一种面向对象的编程结构,它允许封装数据和方法,并将它们组织成一个可重用的代码块。类是创建对象的蓝图,其中包含了对象的属性和方法。

在 TypeScript 中,类可以包含构造函数、属性、方法和访问修饰符等。

class Person{
  myName: string 
  constructor(n:string) {
    this.myName = n
  }
  getName() {
    return this.myName
  }
}


class Male extends Person {
  age:number
  constructor(name:string,age:number){
    super(name) // 传参调用父类的 constructor
    this.age = age 
  }

  // 重写(子类的 方法名与父类同名,会重写这个方法,在调用时会优先调用子类的同名方法)
  getName() {    
    return 'my name is ' + this.myName
  }
}

let me  = new Male('yiyi',18)
console.log(me.myName)
console.log(me.getName())

类的修饰符

类里面 定义的属性  默认的修饰符是public

public 修饰的属性 方法 都可以在类的内部、类的外部、子类中访问 protected 受保护  在类的内部、子类中被访问  (类外部不能访问) private 私有的  只能在类的内部被访问, (子类和类的外部都不能) readonly  设置属性只读, 不能被修改 **static 静态成员 (静态属性) **供类使用

抽象类

抽象类是 TypeScript 中的一种特殊类,它不能直接被实例化,而是用于作为其他类的基类。

抽象类可以包含抽象方法,这些方法只有定义,而没有具体实现。

子类必须实现这些抽象方法,否则会导致编译错误。

注意:抽象类无法被实例化 !!!

// 抽象类  对普通类的描述 ,指定了一个类的规范, 给普通类去继承
// 继承之后 普通类里就必须包含抽象类中的抽象属性 和 抽象方法
// 抽象类中的普通方法直接继承,在普通类中不用实现
abstract class Person {
  abstract name: string
  abstract getName():string
  getAge(){
    return 18
  }
}


//  普通类继承自 抽象类
class Male extends Person {
  name:string = ''
  getName() {
    return this.name
  }
}


let person = new Male()


console.log(person.getAge())


// 抽象类无法被实例化 !!!
let p1 = new Person()    // 报错

抽象类中的普通方法可以被子类直接继承,而不需要在子类中实现这些方法。

这是因为抽象类中的普通方法已经有了具体的实现,子类可以直接使用这些实现。

通过接口 规范类 在 TypeScript 中,可以使用接口来规范类的结构。接口定义了一个类应该具有哪些属性和方法,但不提供具体的实现。类可以实现一个或多个接口,从而满足接口定义的要求。

// 接口 给类使用  规定了 类的属性和方法

interface PerInter {
  name: string,
  age?: number,  // 缺省
  getName:()=> void
}
class Female implements PerInter {
  name:string = ''
  age:number = 18
  getName(){

  }
}

工具类型

Partial

Partial 设置接口中的属性为可缺省属性

interface PerInter {
  name:string
  age:number
}


// Partial 把 <> 里得接口类型的属性设置为可缺省的属性

let  obj:Partial<PerInter>={
  name:''
}

// Partial  相当于  type Partial<T> = { [P in keyof T]?: T[P] | undefined; }
keyof T  'name'|'age'
{
  name?:string|undefined
  age?:number|undefined
}

Required

Required 设置接口中的属性为必填属性

interface PerInter {
  name:string
  age:number
  height?:number
}


// Required 把 <> 里得接口类型得属性设置为必填属性
let obj2:Required<PerInter>={
  name: 'haha',
  age: 12,
  height: 168
}
// Required  type Required<T> = { [P in keyof T]-?: T[P]; }
// keyof T  'name'|'age'|'height'
// -? 抵消? 即 去掉 缺省 变成必填属性
// {
//   name:string
//   age:number
//   height:number
// }

总结

以上是一些关于 TypeScript 类型的基本内容, 想要进阶学习 ts 的小伙伴可以看看这几个优秀的开源项目,这也是我前两天刚找到的😅,大家一起学习共同进步啊:

网站说明
TypeScriptTypeScript 官网,TS 扩展了 JavaScript ,为它添加了类型支持
TypeScript-tutorialTypeScript 入门教程,循序渐进的理解 TypeScript
TypeScriptTypeScript 使用手册
TypeScript-book-chinese深入理解 TypeScript
clean-code-TypeScript适用于TypeScript的简洁代码概念
TypeScript入门TypeScript 入门的视频教程
TypeScript-tutorialTypeScript 速成教程(2小时速成)
转载自:https://juejin.cn/post/7248199693934739512
评论
请登录