typescript学习总结
typescript学习总结
qq学习讨论群:910316886
https://blog.csdn.net/m0_61044901/article/details/125274523
<!--
安装: npm i -g typescript
tsc -v (查看typescript版本)
c盘用户 下去删除.npmrc文件
npm config set registry " https://registry.npm.taobao.org
将ts编译为js,在终端输入命令,tsc hello.ts
执行js代码:在终端输入命令,node hello.js
-->
<!--
简化执行步骤:
# 初始化package.json 文件
npm init -y
# 局部安装 ts-node-dev typescript 依赖
npm install ts-node-dev typescript
# 在package.json 中的scripts中添加脚本
"start": "ts-node-dev --respawn --transpile-only ./src"
# 在终端中就可以使用npm start 启动项目
npm start
npm i -g ts-node
ts-node hello.ts
-->
类型推断
let b=100;
b ='' //报错,只能是number类型,不能赋其他类型的值
any和unknown用法一样
<!--
数组类型的两种写法
let numbers:number[]=[1,2,3]
let numbers2:Array<number>=[2,3,45,5]
let strings:Array<string>=['a','b','c']
let b:boolen[]=[false,true,false]
既有数组又有字符串的类型(联合类型:由两个或多个类型组成的类型)
let arr:(number|string)[]=[1,'a',3,'b'];
-->
<!--
类型别名
CustomArray这个是自己定义的任意合法的变量名
创建类型别名后,直接使用该类型别名作为变量的类型注释即可
type CustomArray = (number|string)[]
let arr1:CustomArray=[1,'a',2,'ggg']
let arr2:CustomArray=[3,'o',9,'sddsad']
-->
<!--
函数类型:函数参数和返回值的类型
为函数指定类型的两种方式:
1 单独指定参数,返回值的类型
function add(num1:number,num2:number):number{
return num1+num2;
}
add(1,2); //必须传参数
2 同时指定参数、返回值的类型(只适用于函数表达式)
const add:(num1:number,num2:number)=>number=(num1,num2)=>{
return num1+num2;
}
3 如果函数没有返回值,那么,函数返回值的类型为:void
function greet(name:string):void{
console.log('hello',name)
}
greet('susu')
4 可选参数:在可传可不传的参数名称后面加?(问号)
可选参数只能出现在参数列表的最后,也就是说可选参数后面不能再出现必选参数
function mySlice(start?:number,end?:number):void{
console.log('起始索引:',start,'结束索引:',end)
}
mySlice(1,2)
mySlice(1)
mySlice()
-->
<!--
对象类型
let person:{name:string;age:number;sayHi():void}={
name:'susu',
age:18,
sayHi(){}
}
sayHi():void是一个方法,表示没有返回值;
换行,可以把分号去掉
let person:{
name:string
age:number
sayHi():void
}={
name:'susu',
age:18,
sayHi(){}
}
-->
<!--
接口
当一个对象类型被多次使用时,一般会使用接口(interface)来描述对象的类型,达到复用的目的。
1 使用interface关键字来声明接口
2 接口名称(可以是任意合法的变量名称)
3 声明接口后,直接使用接口名称作为变量的类型
4 因为每一行只有一个属性类型,因此,属性类型后没有;(分号)
interface IPerson{
name:string
age:number
sayHi():void
}
let person:IPerson={
name:'susu',
age:16,
sayHi(){}
}
-->
<!--
interface(接口)和type(类型别名)的对比
相同点:都可以给对象指定类型
不同点:
接口:只能为对象指定类型
类型别名,不仅可以为对象指定类型,实际上可以为任意类型指定别名
interface IPerson{
name:string
age:number
sayHai():void
}
type IPerson={
name:string
age:number
sayHi():void
}
-->
<!--
extends
interface Point2D {x:number,y:number}
interface Point3D extends Point2D {z:number}
-->
<!--
元组:
它确切的知道包含多少个元素,以及特定索引对应的类型
let position:[number,number]=[39.54,116.23]
{
//限制每一个元素的类型,只能有2个元素
let a: [string, number];
a = ['hello',20];
}
-->
<!--
类型推论:
声明变量并立即初始化值,此时,可以省略类型注释
能省略类型注释的地方就省略
决定函数返回值也可以进行推论
类型断言,通过as关键字指定一个更加具体的类型
console.dir($0)打印结果的最后就可以看到元素的类型
-->
<!--
字面量类型
{
//{}表示代码块
//字面量限制取值范围
let color :'red'|'green'|'blue';
color = 'red';
color = 'green';
color = 'blue';
let num:1|2|3;
num = 2;
}
一般用在一组可明确的可选值类型
function changeDirection(direction:'up'|'down'| 'left'| 'right'){
}
changeDirection('up')
-->
<!--
枚举
enum Direction {Up,Down,Left,Right} 默认0,1,2,3
function changeDirection(direction:Direction){
console.log(direction)
}
changeDirection(Direction.Up)
枚举成员初始化值
enum Direction {Up=10,Down,Left,Right} 10,11,,12,13
enum Direction {Up=10,Down=12,Left=14,Right=16}
enum Direction {Up='UP',Down='DOWN',Left='LEFT',Right='RIGHT'}
-->
<!--
class类
class Person{
age:number,
gender='男'
constructor(age:number,gender:string) {//构造函数不需要返回值
this.age = age
this.gender = gender
}
}
const p = new Person(15,'女')
console.log( p.age, p.gender)
-->
<!--
interface Singble{
sing():void
name:sting
}
class Person implements Singable{
name='susu'
sing(){
console.log('哈哈哈')
}
}
解释:implements实现接口
1 通过implements 关键字让class实现接口
2 Person类实现接口singable意味着,Person类中必须提供Singable接口中的方法和属性
-->
<!--
可见性修饰符
1 public
2 protected 仅对其声明所在类和子类中可见
3 private 表示私有的,只在当前类中可见;对其实例对象以及子类也是不可见的
4 readonly 表示只读,用来防止在构造函数之外对属性进行赋值,也不可以用于方法
(需要指定类型,不然就变成字面量了;接口和普通的对象中也可以使用readonly;只读不可赋值)
-->
<!--
类和接口的兼容性:成员多的可以复制给成员少的
函数兼容性(参数少的可以赋值给多的)
-->
<!--
交叉类型:组合多个类型为一个类型
interface Person {name:string}
interface Contact {phone:string}
type PersonDetail = Person & Contact
let obj:PersonDetail={
name:'jack',
phone:'1333'
}
-->
<!--
泛型
泛型在保证类型安全的同时,可以让函数和不同类型的数据一起使用
function id<Type>(value:Type):Type{return value}
const num=id<number>(10)
也可以简写成 const num=id(10)
-->
<!--
泛型类
class GenericNumber<Numype>{
defaultValue:NumType
add:(x:NumType,y:NumType)=>NumType
}
const myNum = new GenericNumber<number>()
myNum.defaultValue = 0
-->
<!--
泛型工具类型
1 Partial创建一个新的类型,并且属性是可选的
interface Props{
id:string
children:number[]
}
type PartialProps = Partial<Props>
let p1:Props={
id:'',
children:[1]
}
let PartialProps{
id:''
}
2 Readonly 所有属性都是可读的
interface Props{
id:string
children:number[]
}
type PartialProps = Readonly<Props>
let p1:PartialProps{
id:'222',
children:[1,2,3]
}
p1.id='2'//报错
3 Pick Pick<Type,keys>从Type中选择一个属性来构造新类型
interface Props{
id:string
children:number[],
title:string
}
type PickProps = Pick<Props,'id'|'title'>
4 Record<Key,Type>构造一个对象类型,属性键为keys,属性类型为Type
type RecordObj=Record<'a'|'b'|'c',string[]>
let obj:RecordObj={
a:['1'],
b:['2'],
c:['3']
}
-->
<!--
索引签名类型
interface AnyObject{
[key:string]:number key是一个占位符,可以换成任意合法的变量名
}
let obj:AnyObject{
a:1,
b:2,
c:3
}
interface MyArray<Type>{
[n:number]:Type
}
let arr:MyArray<number>=[1,2,3,4]
-->
<!--
映射类型:基于旧类型,创建新类型(对象类型)
type PropKeys='x'|'y'|'z'
type Type1 = {x:number,y:number,z:number}
type Type2 = {[key in PropKeys]:number}
type Type3 = {[key in keyof Type1]:number}
索引查询类型
type Props = {x:number,y:number,z:number}
type Prop = Props['x']
type Prop = Props['x'|'y']
type Prop = Props[keyof Props] 拿到所有索引
-->
<!--
类型声明文件
为已存在的js库,提供类型信息
包含内置的ts类型声明文件,库自带的类型声明文件以及github中DefinitedTyped
TS官方文档中提供了一个页面,用来查询@types/*库的类型,在工具里
declare关键字:用于类型声明,为其它地方,已存在的变量声明类型,而不是创建一个新的变量
-->
<!--
type a = 'a'|'b'|'c'
type b = 'b'|'c'
//剔除
type Atype = Exclude<a,b>
let aDate:Atype='a'
//包含
type Btype = Extract<a,b>
let aDate:Atype='b' //或者c
-->
<!--
命名空间:内部模块,主要用于组织代码,避免命名冲突
export namespace A{
interface Animal{
name:string
}
export class Dog implements Animal{
name:string
constructor(theName:string){
this.name = theName
}
eat(){
console.log(`${this.name}在跑。`)
}
}
}
export namespace B{
interface Animal{
name:string
}
export class Cat implements Animal{
name:string
constructor(theName:string){
this.name = theName
}
eat(){
console.log(`${this.name}在玩耍。`)
}
}
}
let dog= new A.Dog('小狗')
dog.eat()
let cat =new B.Cat('小猫')
cat.eat();
import {A} from '文件名'
-->
<!--
类装饰器
方法一:普通装饰器,没办法传参
function logClass(params:any){
console.log(params)
params.prototype.apiUrl = '动态扩展属性'
params.prototype.run=function(){
console.log('我在跑步')
}
}
@logClass //普通装饰器,没办法传参
class HttpClint{
constructor(){
}
getDate(){
console.log('获取数据')
}
}
let http = new HttpClint()
http.run()
方法二:类装饰器,可传参
function logClass(params:any){
return function(traget:any){
console.log(target)
console.log(params)
traget.prototype.apiUrl = params
}
}
@logClass('hello')
class HttpClint{
constructor(){
}
getDate(){
console.log('获取数据')
}
}
let http2 = new HttpClint();
console.log(http.apiUrl)
修改类中的属性和方法
function logClass(target:any){
console.log(target)
return class extends target{
apiUrl:any='我是修改后的数据'
getDate(){
this.apiUrl=this.apiUrl+'......'
console.log(this.apiUrl)
}
}
}
function logProperty(params:any){
return function(target:any,attr:any){
console.log(target,attr)
target.attr = params
}
}
@logClass('hello')
class HttpClint{
@logProperty('http://itying.com')
public apiUrl:string |undefined;
constructor(){
this.apiUrl = '我是构造函数里的apiUrl'
}
getDate(){
console.log(this.apiUrl)
}
}
let http2 = new HttpClint();
console.log(http2.apiUrl)
htttp2.getDate()
-->
<!--
///<reference path='路径'>
最外层的namesapce不能导出,不然会报错
-->
<!--
infer关键字
-->
<!--
bight与symbol
-->
联合类型
{
let a : string|number|string[]|number[] = [1,4];
a = ['a', 'b', 'c', 'd', 'e'];
a = 1,2,3,4,5,6,7,8;
a = 'hello','hi';
}
类型断言
{
//类型转换
let a : unknown = 'hello world';
// let length =a.length //报错:unknown没有length,只要string有
let length1 =(a as string).length //给a转类型,用as装
let length2 =(<string>a).length //用泛型转变
console.log(length1,length2);
}
函数的参数
{
//普通参数
function a (x:boolean,y:boolean):void{}
//可选参数 ?代表可选 但可选不能放在第一个,必须放在必选参数之后
function b (x:number,y?:number){}
//默认参数
function c (x:number,y:number = 1000){}
//剩余参数
function d (x:number,y:number ,...args:number[]){
console.log(x,y,args);
}
a(true,false);
b(100);
b(7,23);
c(100);
c(100,100);
d(10,20,1,2,3,4);
}
TypeScript抽象类
1)什么时抽象类?
被abstract修饰的类,就是抽象类,抽象类又叫基类
2)为什么要有抽象类?
为了规定类中的一些属性和方法,在被继承的时候必须重写,所以被abstract修饰的方法和属性,在继承时必须重 写,分别叫做抽象类和抽象方法
3)抽象类的特点:
抽象类自身无法实例化,必须由子类(派生类)进行实例化
抽象类也可以拥有自己的抽象方法和属性
抽象类中的抽象方法和抽象属性必须被重写
{ //被abstract修饰的类叫做抽象类 //抽象类不能实例化(创建对象),只能作为基类(父类),派生类(子类) //只能被继承,继承的时候需要重写父类中的抽象属性和抽象方法 //被abstract修饰的属性叫做抽象属性,不能直接赋值 //被abstract修饰的方法叫做抽象方法,不能有方法体 //抽象属性和属性方法不能有具体的实现 //抽象类用来定义类的结果(属性,方法) abstract class Person { abstract name: string; abstract sayHi(); } //继承抽象类 class Student extends Person { name: string; constructor(name: string){ super(); this.name = name; } sayHi() { console.log('${this.name}:hi'); } } const stu = new Student('chen'); }