TypeScript 这一片就够了!
一 、简介
TypeScript(简称TS)是一种由微软开发的开源编程语言。它是JavaScript的一个超集,意味着任何有效的JavaScript代码也是有效的TypeScript代码,同时TypeScript在JavaScript的基础上添加了静态类型、接口、类等新特性。使用ts编写代码的时候静态类型校验,更加严谨。
// 1 安装
npm i -g typescript
// 2 在项目文件夹中执行 tsc - init 表示初始化项目
tsc - init
二 、原始类型
ts原始类型就是js基本数据类型这些 (number,string,Boolean,undefined,null,symbol)
let num: number = 10;
let str: string = "Hello";
let bool: boolean = true;
let n: null = null;
let u: undefined = undefined;
let sym: symbol = Symbol("key");
//在ts中函数没有返回值 函数类型就是void
function a():void{}
三 、非原始类型
1 对象
object 不包含基本数据类型 (常用)
Object与 {}定义都一样 效果一样
//object 不包含基本数据类型
let obj:object = {a:1}
let obj1:object = [1,2]
let num:object = 1 //报错
let str:object = 1 //报错
//Object 包含基本数据类型
let obj:Object = {a:1}
let obj1:Object = [1,2]
let num:Object = 1
let str:Object = 1
//与Object 效果一样 包含基本数据类型
let obj:{} = {a:1}
let obj1:{}= [1,2]
let num:{} = 1
let str:{} = 1
2 数组
// 数组的元素一般数据类型都一致,便于处理
let arr :number[] = [1,2,3]
let arr3 :{a:number,b:bumber}[]=[{a:1,b:2},{a:1,b:2}]
//泛型 类型参数化 (后面详细说)
let arr1 :Array<number> = [10,20]
// 元组
let arr2 :[number,boolean]=[10,true]
3 联合类型 |
//表示数字或者字符串类型
let numAndstr:number|string = 1
//1|'2'在这里的1和'2'是类型 常量 表示numAndstr1的值只能是1|'2'
let numAndstr1:1|'2'= 1
//表示要么有a属性 或者要么有b属性 并且a属性的值必须是常量1 b属性的值必须是字符串
let obj:{a:1}|{b:string};
obj = {c:1} //报错
obj ={a:1}
obj ={a:2} //报错
obj ={ b:'1'}
obj ={b:1} //报错
4 交叉类型 &
let a:number&string; //不会有任何值满足这个类型 一般不会这么写
//& 都 必须有a属性和b属性
let obj : {a:string}&{b:number};
obj = {a:'1'} // 报错
obj = {b:1} //报错
obj = {a:'1',b:1}
5 接口
接口 :用于自定义类型
//定义接口类型 ---给对象用
interface Myitf{
//属性名:值的类型
name:string;
age:number;
}
let obj :Myitf
obj = {
name:'李白',
age:21
}
// 定义接口类型 ---给数组用
interface Arrint{
// [idx:number]下标类型:值类型
[idx:number]:number|string
}
let arr :Arrint
arr = [1,2,'3']
// 定义接口 ---给函数用
interface fnint{
//形参及类型 : 返回值类型
(p:string):void
}
let fn:fnint = (p:string)=>{
}
fn('1')
接口的继承 ,同名 ,缺省 ,只读, 见下方-----------接口
6 联合交叉类型
&与| 交叉使用直接混合双打(& 优先于 |)
let obj :{name:string}&{age:number} | {pname:string}&{page:number};
// &优先级高于 | 相当于let({name:string}&{age:number} )| ({pname:string}&{page:number});
obj={
name:'libai',
age:18
}
obj={
pname:'libai',
page:18
}
7 ts 函数
// 定义函数参数为字符串 并且返回值为数字
function fn(p:string):number{
return 1
}
fn('参数为字符串')
// 使用接口定义函数类型
interface fnine {
// 定义参数为数字 返回值会字符串的函数
(a:number):string
}
let fn1 :fnine=()=>{
return '1'
}
// fn1() //报错 需要传个数字类型参数
fn1(1)
// 使用类型别名定义函数类型
type fntype = (a:number)=>void
let fn2 :fntype =()=>{}
// fn2() //报错 需要传个数字类型参数
fn2(1)
ts函数参数的写法:
// 代表不传就使用默认值3
function fn (a:number,b:number=3){
return a+b
}
console.log(fn(1,3));//输出4
console.log(fn(1));//输出4
// 缺省
function fn1 (a?:number,b:number=3){
return b
}
console.log(fn(1,3));//输出3
console.log(fn( 1));//输出3
// 剩余参数接受
function fn2 (a:number,b:number=3,...arr:number[]){
console.log(a,b);//输出1,3
console.log(arr);//输出[3,4,4,4,]
}
console.log(fn2(1,3,3,4,4,4,));//输出3
ts中的promise
interface resint{
code:number,
data:{a:number,b?:number}[],
msg:string
}
// promise对象: p对象名:promise<res的类型>
let p:Promise<resint> = new Promise((resolve,reject)=>{
resolve({
code:0,
data:[{a:1,b:3},{a:1,}],
msg:''
})
})
p.then(res=>{
if(res.code==0){
res.data.map(item=>item.a)
}
})
this指向问题
ts提供了Window类型,window就是这个Window类型的对象
//在全局上,给Window接口上扩展这个属性
interface Window{
myname:string,
}
function person (this:Window,name:string){
//在ts书写中,需要指明this指向 在函数第一形参位置注明
//Window类型中没有myname这个属性 所以自己通过同名接口来扩展这个属性
this.name = name
}
window.person("")
让this指向自定义对象
interface objint {
name:string,
person:(m:string)=>void
}
let obj:objint = {
name:'李白',
person:()=>{}
}
//定义函数的时候,this的类型 必须和调用的时候类型一致
//this:objint|Window这样写好处是可以给多种类型所对应的对象 让this去指向
function person(this:objint,m:string){
this.name = m
}
obj.person('heiheihei')
四 、any &&unknown
不推荐使用any (不到万不得已不使用,any绕过类型校验)
unknown 一个未知的类型 执行类型校验
//不报错跳过类型校验
let a :any ;
a = 1
a= 'str'
a.toFixed(2);//不报错
let b :unknown ;
b = 1
b= 'str'
b.toFixed(2);//报错 有做类型校验 除非上面写number才不会报错
// 进行类型检查后再赋值
if (typeof b === 'string') {
userName = userInput; // 这里不会报错,因为已经确定了 b 的类型是 string
}else if (typeof b === 'number'){
b.toFixed(2)// 这里不会报错,因为已经确定了 b 的类型是 number
}
五 、never类型
在 TypeScript 中,never 类型表示那些永远不会发生的值的类型。通常情况下,它用于函数的返回类型,表示该函数永远不会正常返回,或者抛出了异常。
1 函数永远不会返回:
function error(message: string): never {
throw new Error(message);
}
2 函数中的无限循环:
function infiniteLoop(): never {
while (true) {
// 无限循环
}
}
3 类型保护中的完整性检查:
function throwError(message: string): never {
throw new Error(message);
}
function checkNumber(x: string | number) {
if (typeof x === 'string') {
// 在这里,x 被缩小为 string 类型
throwError('Not a number: ' + x);
} else {
// 在这里,x 被缩小为 number 类型
return x * 2;
}
}
六 、接口(interface)
1 继承extends
接口继承的格式,特点是具有父接口的属性类型
interface NameItf {
name:string
}
interface AgeItf {
age:number
}
// 继承上两个接口的属性
interface personItf extends NameItf,AgeItf{
height:number
}
let obj:personItf;
obj = {
height:180,
name:'李白',
age:18
}
2 同名
接口可以同名,特点是 合并了所用属性
// 接口同名
interface Myitf {
age:number
}
interface Myitf {
name:string
}
let obj :Myitf = {
age:18,
name:'李白'
}
3 缺省
属性名? 表示这个属性可以缺省 也就是可有可无(定义数据时不写也可以)
// 接口缺省
interface Myitf {
age?:number //属性名? 表示这个属性可以缺省 也就是可有可无
name:string
}
//以下两种都不报错
let obj :Myitf = {
//age:18 定义数据时不写也可以
name:'李白'
}
let obj1 :Myitf = {
name:'李白',
age:18
}
4 只读
接口只读 readonly 表示这个属性只用于读取 不能用于修改
interface Myitf {
age:number
readonly name:string
}
let obj :Myitf = {
name:'李白',
age:18
}
obj.age =19 //可以修改成功
// obj.name = '改不了' //只读属性不可以修改
七 、类型别名(type)
type 用于自定义类型
与interface的区别:类型别名不支持重复定义 ,接口可以重名
类型别名支持联合交叉类型定义
//定义一个类型
type objtype = {a:number&2,b:string}
let obj:objtype = {
a:2,
b:'1'
}
interface objint{
name:string
}
// objint['name'] 用类型别名保存接口上的某个属性类型
type atype = objint['name']
let str :atype='10'
//类型别名支持联合交叉类型定义
type color = 'red'|'pink'|'green'|string&{}
let c :color = 'red'
c = '字符串'//可以
c = {} //报错
八 、枚举(enum)
枚举不是用来定义类型的,是用于列出数据的就,把所有情况列举出来,
//定义枚举
enum StausCode{
success=200,
paramsError=400,
serverErroe =500
}
let code:string|number = 400
if(code ==StausCode.success){
console.log('成功')
}else if (code ==StausCode.paramsError){
console.log('失败,请求参数问题')
}else if (code ==StausCode.serverErroe ){
console.log('失败,服务器问题')
}
//不写默认加一
enum StausCode1{
success, //0
paramsError=400, //400
serverErroe //401
}
console.log(StausCode1.success,StausCode1.paramsError,StausCode1.serverErroe )
九、泛型
在 TypeScript 中,泛型(Generics)是一种能让你在定义函数、类、接口等可重用组件时,支持参数化类型的特性。通过泛型,你可以编写与类型无关的代码,以增强代码的灵活性和重用性。
//等定义的函数参数和返回值一样时
function fn (n:number|boolean):number|boolean{
return n
}
fn(100)
fn(true)
//泛型 可以理解为类型的形参 T是一个标识符 可以自定义 T表示某种类型
//类型参数化
function<T> fn1 (n:T):T{
return n
}
fn1<number>(100)
fn1<boolean>(true)
//泛型在类型别名上的应用
//type objtype = {name:string,age:number,getname:()=>string}
//let obj:objtype={name:'Libai',age:18,getname(){return 1}}
//使用泛型
let STRorNUM= string|number
type objtype<N,G>={name:N,age:G,getname:()=>N}
//let obj:objtype<string,number>={name:'Libai',age:18,getname(){return 1}}
//这里面也可以写类型别名
let obj:objtype<STRorNUM,STRorNUM>={name:'Libai',age:18,getname(){return 1}}
//泛型在接口上的应用
//interface objint {age:number,fn:()=>string}
//let obj1 :objint = {age:18,fn(){return '字符串'}}
//使用泛型 B=number为设置默认值 可以设置默认的类型 在使用时可以省略不传
interface objint<A,B=number> {age:A,fn:()=>B}
let obj1 :objint<STRorNUM,string> = {age:18,fn(){return '字符串'}}
//泛型的约束extends
interface PersonInt<N extends string|number,G>{
name:N;//需求 :N只能接受字符串或者数字
getname:()=>G
}
//N在这里只可以传string和number 传其他就报错
let obj :PersonInt<string,number>={
name:'1',
getname(){return 1}
}
十、 类
1 类的定义(类-接口)
//定义类的同时,相当于定义了相同名称的接口
class Person{
//定义属性前,应该先声明这个属性的类型,也可以同时设置默认类型
age:string='默认名称';
name:string;
//使用 new 关键字创建类的实例时,constructor构造函数会自动被调用
constructor(n:string){
this.name =n
}
getname(){console.log(this.name)}
}
let p = new Person('李白')
console.log(p.name) //输出 李白
console.log(p) //输出 {name:'李白',age:'默认名称',getname:f()}
//以上这个类相当于下面这个接口
//interface person {
// name:string;
// age:'默认名称';
// getname:()=>string
//}
let p1:Person = {
age:'',
name:'',
getname(){
console.log(this.name)
}
}
2 类的继承
//定义父类
class animal{
name:string;
age:number;
bark(){
alert('动物在叫!')
};
constructor(n:string,a:number){
this.name=n
this.age =a
}
}
// 定义cat类 继承于父类animal
class cat extends animal{
// 可以增加自己独有的 属性和方法
run(){
console.log('我在跑');
};
// 如果在子类中添加了父类相同的方法 则子类会覆盖掉父类的方法 这种叫做方法重写
bark(){
alert('猫在叫!')
// 在类的方法中super代表当前类的父亲 此处执行父亲的bark方法
super.bark()
};
// 如果在子类中写了构造函数 也会发生方法重写,所以父类中的函数不会执行,所以要通过super()调用父类函数的构造函数
constructor(name:string,age:number){
super(name,age)
}
}
let cat1 = new cat('猫',12)
console.log(cat1); //{name:'猫',age:12}
console.log(cat1.run());//我在跑
cat1.bark() //弹出猫在叫 动物再叫
3 类修饰符
class animal {
//类中,定义的属性 默认的修饰符就是public public修饰的属性和方法在类内部,外部,子部都可以访问
// protected 受保护的 类里面 子类都可以访问 类的外面不能访问
// private 私有的 只有在当前类访问 子类和类外面都不能访问
name:string;
public age:number;
protected weight:string;
private run :string
constructor(n:string,a:number,k:string,r:string){
this.name=n
this.age =a
this.weight = k
this.run = r
}
}
let dog = new animal('狗',12,'12kg','跑步')
console.log(dog.name);
console.log(dog.weight);
console.log(dog.run);
类的静态属性
class pre{
// 静态属性/成员 是给类去用的 只用当前类
static title:string="title的值"
}
console.log(pre.title) //title的值
pre.title = "title修改后的值"
console.log(pre.title) //title修改后的值
4 抽象类
// 抽象类 是类的描述 指定一个类的规范,给普通的类去继承
abstract class person{
abstract name:string;
abstract getname():string;
getage(){
return 11
}
}
// 普通类继承之后 ,普通类里面就必须定义抽象类里面的抽象属性和抽象方法,
// 抽象类里面的普通方法直接继承,在普通类里可以不用实现
// 普通类
class a extends person{
name: string="xxx";
getname(){
return this.name
}
}
//抽象类不能被实例化报错
// let p1 = new person()
let m1 =new a()
console.log(m1.getname()) //xxx
console.log(m1.getage())//11
// 定义接口给类使用 implements
interface preint{
name:string;
age:number;
getname:()=>void
}
class m implements preint{
name:string = "";
age:number = 0;
getname(){
return 11
}
}
let mm = new m()
十一、 工具类及其他
1 Partial
部分的 表示每个属性都可有可无
interface preint {
name:string;
age:number;
}
// 以下都不会报错
//作用 把<>里面这个类型 设置为可缺省
let per1:Partial<preint> = {
name:'libai'
}
let per2:Partial<preint> = {
age:16
}
let per3:Partial<preint> = {
name:'libai',
age:16
}
let per4 :Partial<preint> = {
}
2 Required
必需有的 把<>里面这个类型 设置为不可缺省
interface preint {
name:string;
age?:number;
}
let per5:Required<preint>={
name:'libai',
age:18
}
let per6:Required<preint>={
age:18
} //报错
let per7:Required<preint>={
name:'libai',
} //报错
类型断言:
在 TypeScript 中,类型断言(Type Assertion)是一种告诉编译器某个值的类型的方式。它类似于其他编程语言中的类型转换。通过类型断言,你可以在编译期告诉 TypeScript 编译器某个值的确切类型,并且不需要实际更改该值的结构。
在 TypeScript 中,有两种类型断言的语法:
//第一种
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
//第二种
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
类型断言的作用主要有两个方面:
1 类型转换: 当 TypeScript 编译器无法自动推断出某个值的类型时,或者你比编译器更了解某个值的类型时,你可以使用类型断言将该值断言为你期望的类型。
2 解决类型不兼容问题: 在编写泛型代码或者处理联合类型时,有时会遇到类型不兼容的情况。通过类型断言,你可以告诉编译器如何处理这种不兼容,从而使代码能够顺利编译。
需要注意的是,虽然类型断言可以让 TypeScript 编译器认为某个值具有特定的类型,但实际上并不会在运行时对值进行类型转换。因此,在进行类型断言时,务必确保你对值的类型了解清楚,以避免出现运行时错误。
keyof 和typeof和in
keyof
type Person = {
name: string;
age: number;
address: string;
};
//把每一个键值都取出来了 keyof后面一般是接口或者type的对象(Person) 实际上就是:前面的
type PersonKeys = keyof Person; //'name' | 'age' | 'address' 都是常量
let a:PersonKeys ='name' //'name' | 'age' | 'address'其中一个
typeof :typeof 是一个类型查询操作符,用于获取一个值的类型。
let x = 10;
let y: typeof x; // 此时 y 的类型为 number
in
//1定义对象的键值可以为所有字符串和数字类型
type pr = {
[k in string|number]:string
}
let pr1:pr = {
a:'1',
2:'2'
}
//2检查对象是否具有指定属性:
interface Person {
name: string;
age: number;
}
let person: Person = { name: 'John', age: 30 };
if ('name' in person) {
console.log('person对象中有name属性');
}
if ('address' in person) {
console.log('person对象中有address属性'); // 这行不会被执行,因为 person 对象没有 address 属性
}
//3检查指定属性是否存在于对象的原型链中:
class Animal {
eat(): void {
console.log('Animal is eating');
}
}
class Dog extends Animal {
bark(): void {
console.log('Dog is barking');
}
}
let dog = new Dog();
if ('eat' in dog) {
console.log('Dog can eat');
}
if ('bark' in dog) {
console.log('Dog can bark');
}