首页 前端知识 TS简单介绍以及用法

TS简单介绍以及用法

2024-08-15 22:08:00 前端知识 前端哥 461 415 我要收藏

TS简单介绍以及用法

一、什么是TS

TS是TypeScript的缩写,是JavaScript的超集 (JS有的TS都有),也可以说TS是 type + js,加了一个类型。比如:JS:let a = 0,TS:let a:number=0

为什么需要给JS添加type呢?因为js的类型系统存在“先天缺陷”,JS代码中绝大部分的错误都是类型错误,那么就会存在一个问题,那就是找bug、改bug的时间增加,严重影响了开发效率。

从编程语言的动静来区分;TS属于静态类型的编程语言,JS属于动态的编程语言。

  • 动态编程语言:执行期间做类型检查
  • 静态编程语言:编译期间做类型检查

TS相对于JS的优势:

  • 更早发现代码中的错误,减少找bug、改bug的时间,提升了程序员开发效率。
  • 程序中任何位置的代码 都有 代码提示, 知道参数的类型是哪些,增加了开发体验。
  • 强大的 类型系统 提升了代码的可维护性,使得重构代码更加容易。
  • 支持最新的ECMAscript语法,体验最新的语法,让你走在前端开发的前沿。
  • TS类型推断机制,不需要你在代码中的每一个地方都添加类型标注,降低了开发成本。

二、TS的常用类型

  • 原始类型:number、string、boolean、null、undefined、symbol
  • 数组类型:两种写法:1、number[] 2、Array< number > 定义两个:(number|string)[]
  • 对象类型:1、需要申明对象中的属性,例如:let obj :(name:string;age:number;sayWorld(a:string):void)= {name:'你好',age:22,sayWorld(a){}},
    2、可选参数和下面函数写法一致
  • 类型别名:type 如下:
type stateArray = ( number | string )[]
let a: stateArray =[1,2,'a']
let b: stateArray =[3,4,'a',5]
  • 函数类型:1、单独制定参数、返回值类型 , 2、同时指定参数和返回值的类型
    特殊的函数类型 void (表示没有返回值)
    可选参数 在参数后面加一个问号 可选参数只能放在必选参数后面
//1、单独制定参数、返回值类型
function fn(num1:number,num2:number):number{
	retuen num1+num2
}
const fun = (num1:number,num2:number):number=>{
	retuen num1+num2
}
//2、同时指定参数和返回值的类型
const fun1:(num1:number,num2:number)=>number = (num1,num2)=>{
	reurn num1+num2
}
//可选参数 没有返回值
const fun = (num1:number,num2?:number):void=>{
}

  • 接口 interface关键词
interface state {
	name:string;
	age:number;
	sayHi():void
	number?:number;
}
let obj:state  ={
	name:'你好',
	age:'22',
	sayHi(){},
}

我们发现 type和interface的用法和写法非常像,其实两者是有区别的,
interface只能为对象指定类型
type不仅可以为对象指定类型,实际上可以为任何类型指定类型。

  • 接口 extends 关键字 例如:
interface state2D{x: number; y: number}
interface state3D extends state2D{z:number}
let d3: state3D ={
	x:1,
	y:2,
	z=3
}
  • 元组类型

元组类型是另一种类型的数组,他知道数组中到底有多少个元素,以及特定索引对应的类型let array:[number,number]=[1,2]

  • 类型推论

在TS中,某些没有明确指出类型的地方,TS中的类型推论机制会帮助提供类型。也就是有些地方可以省略不写!
发生类型推论的常见地方有两个:1、声明变量并初始化的时候 2、决定函数返回值
在实际开发中,能省则省

//1\声明变量并初始化的时候 
let num = 10 //number类型
let str = 'string'//string类型
num = '10'//报错
 //2、决定函数返回值
 function add(num1:number,num:number){ //类型推断是 number
 return num1 + num2
 }

到这里,又有人迷糊了,这不是和js是一样的吗?其实还是不一样的,在ts中 变量声明并且马上给初始值时。ts的类型推断会自动加上,在后面就不能赋值其他类型,否则会报错,js则不会

  • 类型断言 as

有时候你会比TS更加明确一个值的类型,此时,可以使用 类型断言 来指定更具体的类型。比如:

//html
<a herf="http://baidu.com" id="linl">百度</a>
//TS
 const aLink = document.getElementById('link) //aLink : HTMLElement
 //此时 aLink是HTMLElement 类型,只有一些基本的属性,访问不到 a标签的herf等属性
 // 此时我们需要用类型断言来解决这个问题
 const aLink = document.getElementById('link) as HTMLAnchorElement
 //使用 as 关键字实现类型断言
 //关键字 as 后面的类型是一个更加具体的类型 (HTMLAnchorElement 是 HTMLElement 的一个子类型)
 //通过类型断言,aLink的类型变得更加具体,这样可以访问a标签的特有属性和方法了
 
 //另一种写法
 const aLinl = <HTMLAnchorElement>document.getElementById('link') //react中用不了 冲突

到这里就有人会问了,我怎么知道这些到底 具体是什么元素呢 这时候我们就可以利用浏览器打印来知道具体的类型,在审查元素选中标签, console.dir($0) 打印,saajbv点击打开 滑倒最下面可以看到了,英语比较好的 直接写就好了。

  • 字面量类型
    不同的声明方式,在TS中会有不同的数据类型,比如:
let str1 = 'Hello TS' //String 数据类型
const str2 = 'Hello TS' // Hello TS 数据类型

这是为什么呢?因为 let声明的是一个变量,可以更改值,所以类型为:string . 而const声明的是一个常量,不能更改值,值只能是Hello TS,所以TS默认他的数据类型是Hello TS

使用场景:用来表示一组明确的可选值列表,比如需要声明一个函数,参数只能是 上下左右

function fun (direction:'up'|'down'|'left'|'right'){
}
fun('up')
//此时 fun函数只能接收 up down left right 四个参数 否则报错
  • 枚举类型

枚举类型 就是字面量类型+联合类型组合的功能,也可以表示一组明确的可选值
枚举:定义一组命名常量。他描述一个值,改值可以是这些命名常量中的一个。

enmu Direction { Up,Down,Left,Right} //逗号分隔,不是| 枚举值以大写字母开头
function fun (direction :Direction){
}
//形参direction 的类型为枚举Direction,那么实参的值就应该是Direction 中成员的任意一个。
//枚举成员是有值的,是从0开始的自增数 Up:0 Down:1 Left:2 Right:3
// 当然,也可以也可以给枚举中的成员初始化值。 enmu Direction { Up=2,Down=4,Left=6,Right=8}
fun(Direction .Up)
//如果enmu Direction { Up=2,Down,Left,Right} 那么 Down:3 以此类推
//访问枚举成员直接使用点(.)语法。类似js中的对象.

当然,枚举成员的值也可以是字符串,如果是字符串的话 那么枚举所有成员的初始值都需要被定义,因为字符串没有自增长行为。

  • any类型 (不推荐使用)

这会让 'TypeScript’变成 ‘AnyScript’ (失去TS的类型保护的优势),因为当值的类型是any时,可以对该值进行任意操作,并且不会有代码提示,
具有隐式 any 类型的情况:声明变量不提供类型也不提供默认值,函数参数不加类型

  • typeof 操作符

众所周知,在js中 typeOf 是用来获取数据类型的,实际上,TS也提供了 typeOf 操作符:可以在类型上下文中引用变量或属性的类型(类型查询)。当然在TS中也可以使用JS中的用法。两者区别如下:

let p = {
	a: 1,
	b: 2,
}
//js
console.log(typeof p) //打印 'object'
//TS
function fun(point:{a:number,b:number}){}
fun(p)
//可以用typeof 进行优化
function fun (point: typeof p){}
fun(p)
//当然 也可也获取对象中的类型
let c:typeof p.a //c的类型就是number

三、TS的高级类型

  • class类

相对于JS中class类,在TS中添加了几种方法: implements(实现接口)、可见性修饰符:pubilc(公有的),protected(受保护的),private(私有的),readonly(只读)。

1、JS里的使用
//JS中的基本使用
class Person{
	name: string //没有默认值的 需要添加类型 如果没有添加 会默认为 any 
	//age = 18 //如果有默认值,不需要添加类型,有类型推断 会自动默认为number
	// age:number = 18  //这样写也是可以的 不过我们能省则省
	age :number
    //构造函数
	constructor(name:string,age:number){
		this.name = name
		this.age = age
	}
    //实例方法
    AddAge(n:number){
        this.age +=n
    }
}
const p = new Person('张三',18) //实例对象 P 的类型就是 Person
p.AddAge(2)
console.log(p)// Person { name: '张三', age: 20 } 用 . 可以访  具体属性'

// 继承 extends 
class Son extends Person{
    rideAge(n:number){
        this.age *=n
    }
}
const s = new Son('李四',18)
console.log(s.name) //李四
console.log(s.age) //18
s.AddAge(1)
console.log(s.age) //19
s.rideAge(2)
console.log(s.age) //38
2、 implements(实现接口)
// implements
interface state{
    sing():void
}
class Person implements state{
    sing(){
        console.log('唱歌')
    }
}
3、可见性修饰符
(1)、public(公开的)
// 默认就是 public(公开的) 写不写都可以
class Example {
    public move(){
        console.log('移动了')
    }
}
(2)、protected(受保护的)
//protected 受保护的 只能在class本身 或者子类中使用
class Person {
    protected move(){
        console.log('移动了')
    }
}
class Son extends Person{
    run(){
        this.move() // 正常  可以调用到
    }
}
const p =new Person()
p.move // 报错 属性“move”受保护,只能在类“Person”及其子类中访问
const s =new Son()
s.move // 报错 属性“move”受保护,只能在类“Person”及其子类中访问

(3)、private(私有的)
//private(私有的) 只能在当前class内部使用
class Person1 {
    private move(){
        console.log('移动了')
    }
}
class Son1 extends Person1{
    run(){
        this.move() // 报错 属性“move”为私有属性,只能在类“Person1”中访问
    }
}
const p1 =new Person1()
p1.move // 报错 属性“move”为私有属性,只能在类“Person1”中访问
const s1 =new Son1()
s1.move // 报错 属性“move”为私有属性,只能在类“Person1”中访问
(4)、readonly(只读属性)
//readonly 只读属性
class Person2{
     //如果属性设置为 readonly 需要指定类型 
    //  不指定类型又有初始值时 类型会默认为初始值 
    // 不指定类型又没有初始值时,类型为any
    readonly age:number = 18
    constructor(age:number){
        this.age = age
    }
    // 错误演示 readonly不能对方法使用
    // readonly say(){} //报错"readonly" 修饰符仅可出现在属性声明或索引签名中。
}
// readonly 不仅仅可以使用在class类里面 还可以在接口interface 和{}类型声明 里面使用
interface state {
    readonly name:string
}
let state:state = {
    name:'1'
}
state.name = '2' // 报错 无法分配到 "name" ,因为它是只读属性。

let obj:{ readonly name:string} = {
    name:'1'
}
obj.name = '2' // 报错 无法分配到 "name" ,因为它是只读属性。
  • 类型兼容性

两种类型系统 1、Structural Type System(结构化类型系统) 2、Nominal Type System(标明类型系统)
TS采用的是 结构化类型系统,类型检查关注的值所具有的形状
在TS中,主要是 对象、接口、函数的类型兼容性。

  • 交叉类型 (&

交叉类型(&):功能类似于接口继承(extends),用于组合多个类型为一个类型(常用于对象类型)

// 交叉类型
interface Person{
    name:string
}
interface Son{
    age:number
}
type SonD = Person & Son
let S :SonD ={
    name:'张三',
    age:18}
let S1:(Person & Son)={
    name:'张三',
    age:18}

在这里可以看出 交叉类型和 继承好像没有什么不同,那我们来说一下两者不同的地方,如果属性或者方法又冲突的时候,两者处理的方式不一样,看下面代码

interface Person{
    say:(a:number)=>string
}
// 继承
interface Son extends Person{
    say:(a:string)=>string
}
// 报错
// 接口“Son”错误扩展接口“Person”。
//   属性“say”的类型不兼容。
//   不能将类型“(a: string) => string”分配给类型“(a: number) => number”。
//     参数“a”和“a” 的类型不兼容。
//       不能将类型“number”分配给类型“string”。

//交叉类型 
interface Son1{
    say:(a:string)=>string
}
type SonD = Person & Son1
let S1:SonD ={
    say(value:number|string){
        return ''
    }
    // (property) say: ((a: number) => string) & ((a: string) => string)
} 

  • 泛型和keyof

泛型是可以在保证类型安全前提下,让函数等与多种类型一起工作,从而 实现复用,常用于:函数,接口,class中。

1、泛型基本使用
function fun<Type>(val:Type):Type{
    return val
}
fun<number>(1) //function fun<number>(a: number): number
fun<string>('1') //function fun<string>(val: string): string
// 简化
fun(1) //function fun<1>(val: 1): 1
2、泛型约束

主要有两种方式:1、指定更加具体的类型 2、添加约束
如果不添加类型约束,那么参数可以是任何的类型,所以没有办法调用任何的方法 所以需要添加类型约束。

两种方式代码如下:

// 1、指定更加具体的类型 比如指定数组,我们就可以访问数组中的.length属性
function fun <Type>(val:Type[]):Type[]{
    console.log(val.length)
    return val
}
fun<string>(["1","2"])
// 2、添加约束 
interface ILength{
    length:number
}
// 只要含有length属性都可以
function fun1<Type extends ILength>(val:Type):Type{
    console.log(val.length)
    return val
}
fun1([1,2])
fun1('123456')
fun1({length:50,age:18})
fun1(123) // 报错 类型“number”的参数不能赋给类型“ILength”的参数

// 泛型变量可以是多个 看下面代码 
// keyof 关键字 接收一个对象类型,生成其键名称(可能是字符串或数字)的联合类型
function fun2<Type ,Key extends keyof Type>(obj:Type,key:Key){
    return obj[key]
}
// 一般情况
let person = {name:'张三',age:18}
fun2(person,'name')
// 了解补充
fun2('asd','charAt')
fun2(12,'toFixed')
fun2('asd',1)

泛型接口和泛型类

// 泛型接口
interface stateId<Type>{
    id:(val:Type)=>Type
    ids:()=>Type[]
}
let obj:stateId<number>={
    id(val){
        return val
    },
    ids(){
        return [1,2,3]
    }
}
// 其实 数组就是一个泛型接口 
let arr = [1,2,3]
arr.forEach(item=>{ //(method) Array<number>.forEach(callbackfn: (value: number, index: number, array: number[]) => void, thisArg?: any): void
})

// 泛型类
class Person<Type>{
    defaultValue:Type
    add:(num1:Type,num2:number)=>number
}
let p = new Person<number>()
p.defaultValue = 10
//可以省略不写
class Person1<Type>{
    defaultValue:Type
    constructor(value:Type){
        this.defaultValue = value
    }
}
let p1 = new Person1(10)

泛型工具类型

在TS中,内置了一些泛型工具类型,来简化TS中的一些常见操作主要知道4个就行:
Partial< Type > 、 Readonly< Type >、 Pick< Type,Key >、Record < Keys,Type >

// Partial< Type > 把已有的全部转换成可选属性
interface state {
    id:number,
    ids:number[]
}
type PartialState =  Partial<state>
// 变换成这样 全部属性变成可选
// type PartialState = {
//     id?: number | undefined;
//     ids?: number[] | undefined;
// }
let P:PartialState = {}

// Readonly< Type >
interface state1 {
    id:number,
    ids:number[]
}
type ReadonlyState1 =  Readonly<state1>
// 变换成这样 全部属性变成可选
// type PartialState = {
//     id?: number | undefined;
//     ids?: number[] | undefined;
// }
let P1:ReadonlyState1 = {
    id:1,
    ids:[1]
}
P1.id = 10 //报错 无法分配到 "id" ,因为它是只读属性。

// Pick< Type,Key > 选择一些属性来构造新的类型
interface state2 {
    id:number,
    ids:number[]
}
type PickState2 =  Pick<state2,'id'>
// 变换成这样 全部属性变成可选
// type PartialState = {
//     id?: number | undefined;
//     ids?: number[] | undefined;
// }
let P2:PickState2 = {
    id:1,
    ids:[1] // 报错 不能将类型“{ id: number; ids: number[]; }”分配给类型“PickState2”。对象文字可以只指定已知属性,并且“ids”不在类型“PickState2”中。
}

//Record <Keys, Type > 构建一个对象类型,属性的键为Keys,属性类型为 Type
type RecordObj = Record<'a'|'b'|1,string[]>
let obj:RecordObj = {
    a:['1'],
    b:['1'],
    1:['1']
}
  • 索引签名类型和索引查询类型

在开发中,当无法确定对象中有哪些属性的时候,就要用到索引签名类型了。

// 对象 对象中的键值一定是字符串类型
interface state {
    [key:string]:string
}
let obj:state={
    a:'1',
    b:'2',
}
// 在TS中 数组是一个特殊的对象 特殊在数组的键(索引)是数值类型
interface MyArray<T>{
    [n:number]:T
}
let arr:MyArray<number> = [1,2,3]  
  • 映射类型

映射类型:基于旧类型创建新类型,减少重复书写、提升开发效率。
映射类型 没办法在interface 接口中使用

type propsKey = 'X'|'Y'|'Z'
// type1 type2 实现效果一样
type Type1 = {x:number,Y:number,z:number}
type Type2 = {[key in propsKey]:number}
// type Type2 = {
//     X: number;
//     Y: number;
//     Z: number;
// }
// 基于对象 keyof 获取键值 相当于 'X'|'Y'|'Z'
type propsKey2 = {x:number,Y:string,z:boolean}
type Type3 = {[key in keyof Type1]:number}
// type Type3 = {
//     x: number;
//     Y: number;
//     z: number;
// }

索引查询(访问)类型

type a = {x:number,b:string,c:boolean}
type b = a['x'] //type b = number
//模拟 Partial 实现

type MyPartial<T>{
    [p in keyof T]?:T[p]
}
type PartialStates = MyPartial<a>
// type PartialStates = {
//     x?: number | undefined;
//     b?: string | undefined;
//     c?: boolean | undefined;
// }

// ---
// 同时查询多个索引的类型
type c = a['b'|'c'] //type c = string | boolean
type d = a[keyof a] //type d = string | number | boolean

感谢浏览观看 关注博主 一起学习!!!

转载请注明出处或者链接地址:https://www.qianduange.cn//article/15695.html
标签
评论
发布的文章

安装Nodejs后,npm无法使用

2024-11-30 11:11:38

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!