TypeScript学习记录

TypeScript:

  1. ​ typescript的定位是静态类型语言,在写代码的阶段就能检查错误,而非运行阶段

  2. 类型系统是最好的学习文档,增加了代码的可维护性

  3. 需要理解接口(interface),泛型(Generics),类(class)等

  4. ts最后会被编译成js

    一、使用

    安装: npm install typescript -g

    新建.ts文件,编译命令: tsc 文件名

    优化编译

    1. 解决ts和js冲突问题: tsc --init //生成配置文件
    2. 自动编译:tsc --watch
    3. 发出错误: tsc -nnoEmitOnError 文件名

    显示类型

    function great(person:String,date:String){
        console.log(`hello,${person},today id ${date}--`)
    }
    great('libai','2021/4/3')
    

    降级编译(默认是以ES2016编译的)

    tsconfig.json:  target:  'es2016' //编译后的代码可以支持es2016的语法
    

    严格模式

    tsconfig.json:

    “strict”: true
    
    "noImplicitAny": true,
    
    "strictNullChecks": true
    

    二、基元类型string,number,boolean,数组,any

    let arr:number[] = [1,2,3,5]
    let arr2 : Array<number> =[1,2,34] //泛型
    arr2 =[1,2,34]
    let str:string = 'hello'
    let num:number = 100
    let bool :boolean = true
    

any: 不希望某个特定值导致类型检测错误,即表示对所有类型都不做校验, 以下代码不会报错

let obj:any={
    x:0
}
obj.foo()
obj()
obj.bar = 100
obj = 'hello'

变量上的类型注释

//变量的类型注释

let myName: string = 'test'

let test = 'test'

//函数的类型注释
function great(name: string){
    console.log(`${name}`)
}
great('test')

//函数的返回值类型注释出现在函数的参数列表之后

function getNumber() : number{
    return Math.random()
}
//匿名函数
//即使我们没有显示定义函数参数类型,ts也会根据上下文推断该参数的类型
//统称为上下文类型
const names = ['test','rts']
//知道函数执行的地方
names.forEach(function (s){

})
names.forEach((item)=>{

})

对象类型

function printCot(temp: {x: number, y: number}){
    console.log(`${temp.x},${temp.y}`)
}
printCot({
    x:1,
    y:2
})
//last?: string表示last是可选的,不是必须传的
//不能传入除已定义的属性之外的属性,否则会报错

function getName(obj:{first: string,last?: string}){
    //console.log(obj.last.toUpperCase()
    if(obj.last !== 'undefined'){
        console.log(obj.last)
    }
    return `${obj.first}${obj.last}`
}
getName({first:'test'})

联合类型

联合类型表示两个或者多个其他类型组成的类型,表示可能是这些类型中的任何一种的值: union类型

联合类型可以使得变量类型范围变宽,但是使用的时候要注意缩小类型范围

function printId(id: string|number|Array<number>){
    // console.log(id?.toUpperCase())
    if(typeof id === "string"){
        console.log(id?.toUpperCase())
    }else if(typeof id === 'number'){
        console.log(id?.toFixed())
    }else {
        console.log(id)
    }
}
printId('101')
printId(101)

function getFirstName(x:string | Array<number>){
    console.log(x.slice(0,3))
}
getFirstName('12345566')
getFirstName([1,2,3,45])

类型别名

类型提前声明: type 变量类型

type Point = {
    x: number,
    y: number
}
function printDot(pt: Point){

}
printDot({
    x:100,
    y:100
})
type ID = number | string
function getId(id:  ID): string{
    return id?.toString()
}

接口: interface

类型别名和接口的区别:几乎所有的接口都可以使用type + 类型别名来定义,区别如下:

​ 接口的扩展使用extends,类型别名的扩展使用&

​ 向接口中添加新字段可以重复定义接口,而类型别名中重复定义类型会报错

interface PointTest {
    x: number,
    y:number
}
function printDotId(pt: PointTest){

}
printDotId({
    x:100,
    y:100
})

//类型别名和接口的区别:
//几乎所有的接口都可以使用type + 类型别名来定义,区别如下:

//扩展接口
interface Animal{
    name: string
}
interface Bear extends Animal{
    honey:boolean
}
const bear: Bear = {
    name:'libai',
    honey: true
}
console.log(bear.name)
console.log(bear.honey)

//type的交叉扩展
type AnimalTest = {
    name: string
}
type BearTest = Animal & {
    honey: Boolean
}
const bearTest: Bear = {
    name:'hello',
    honey:false
}
console.log(bearTest.name)
console.log(bearTest.honey)

//如何向现有的接口或者类型里添加字段
//接口类型可以重新定义重名的接口去添加新的字段
interface MyWindow{
    count: number
}
interface MyWindow{
    title: string
}
const temp: MyWindow = {
    title:'hello',
    count:100
}
//类型创建以后是不能更改的,否则会报错
type MyWindowTest = {
    title: string
}
type MyWindowTest = {
    count: number
}

类型断言

断言: 当不知道某个变量类型时,断言为某个已知的类型

const myCanvas = document.getElementById('myCanvas') as HTMLCanvasElement

const myCanvas2 = <HTMLCanvasElement>document.getElementById('myCanvas')

const x = ('hello' as any) as number
const y = ('hello' as unknown) as number

文字类型

let testString = 'test'
const constantString = 'test'

let ut: 'hello' = 'hello'

//alignment: 'left'|'right'|'center':表示alignment的值只能时这其中的一个
function printText(s:string,alignment: 'left'|'right'|'center'){
    
}
printText('test','left')
//: -1 | 0 | 1表示返回值时-1,0,1其中之一
function compare(a:string,b:string): -1 | 0 | 1{
    return a ===b ? 0: a>b ? 1 : -1
}
interface Options{
    width:number
}
//x: Options|'auto':表示x的值要么时Options类型的,要么是'auto'
function configure(x: Options|'auto'){

}
configure({width:12})
configure('auto')

//变量类型推断

function handleRequest(url: string,method:'GET'|'POST'|'GUESS'){

}
handleRequest(req.url,req.method)//req.method会被认为是一个除get/post/guess之外的特殊字符串
const req = {
    url:'https://test',
    method:'GET' as 'GET'
}
handleRequest(req.url,req.method)//req.method
//或者
handleRequest(req.url,req.method as 'GET')
//或者
const reqtest = {
    url:'https://test',
    method:'GET' as 'GET'
} as const

null和undefined类型

null指的是不存在,undefined表示未初始化的值

let h: undefined = undefined
//类型推断: 因此undefined可以省略
let z:null = null

function doSomething(x:string | null){
    if(x === null){
        console.log(x)
    }else{
        console.log(x.toUpperCase())
    }
}
function live(x?:number | null){
    console.log(x?.toFixed(2))
    console.log(x!.toFixed(2)) //!表示当前值不可能是null或者undefined
}

枚举

通过enum关键字来定义

enum Description{
    left = 1,
    right,
    up,
    down
}
//设置left的值后其余值会自动递增
console.log(Description.left)
console.log(Description.right)

bigint,symbol

bigint: 非常大的整数

symbol: 全局唯一使用的

const tempTest: bigint = BigInt(100)
const anotherInt: bigint = 100n

const firstName = Symbol('name')
const secondName = Symbol('name')

类型

类型缩小:将类型转化为比声明更具体的类型

从宽类型到窄类型的变化,经常用于处理联合类型

typeof 类型守卫

typeof null和typeof Array的值都会是Object

//ts代码
function printAll(strs: string|Array<string> | null){
    if(typeof strs === 'string'){
        console.log(strs.toUpperCase())
    }else if(typeof strs === 'object'){
        strs?.forEach(item => console.log(item))
    }
}
printAll('hello')
printAll('hello'.split(''))

//编译后的js代码
"use strict";
function printAll(strs) {
    if (typeof strs === 'string') {
        console.log(strs.toUpperCase());
    }
    else if (typeof strs === 'object') {
        strs === null || strs === void 0 ? void 0 : strs.forEach(item => console.log(item));
    }
}
printAll('hello');
printAll('hello'.split(''));
真值检查: && || !!
等值检查: === ,!==,< , >
function example(x: string|number, y: number | boolean){
    if(x === y){
        x.toString()
        y.toString()
    }
}
function tets(strs: string | Array<string> | null ){
    if(strs !== null){
        if(typeof strs === 'string'){
            console.log(strs.toUpperCase())
        }else if(typeof strs === 'object'){
            strs.forEach(item => console.log(item))
        }
    }
}
//null == undefined //true
interface Container{
    value: number | null | undefined
}
function test(x:Container,factor: number){
    if(x.value != null){
        console.log(x.value)
    }
}
test({value:5},6) // 5
test({value:undefined},6) // 
in操作符缩小:确定对象是否有某个属性
type Fish ={
    swin: ()=> void,
}
type Bird ={
    fly: ()=> void,
}
type Human = {
    swin?: ()=>void,
    fly?: ()=>void
}
function move(animal: Fish| Bird | Human){
    if('swin' in animal){
        return (animal as Fish).swin() //断言为Fish
    }
    return (animal as Bird).fly() //断言为Bird
}
instanceof操作符缩小:检查对象是否是某个类的实例
function logValue(x: Date | string){
    if(x instanceof Date){
        console.log(x.toUTCString())
    }else{
        console.log(x.toUpperCase())
    }
}
logValue(new Date()) //Wed, 12 Jul 2023 03:11:07 GMT
分配缩小:利用三元运算符进行类型分配
// let x: number | string
let x = Math.random() > 0.5 ? 10 : 'hello'
x = 1
x = 'good'
使用类型谓词

类型谓词: pet is FishTest : 属性 is 类型

type FishTest={
    name:string,
    swim: ()=>{}
}
type BordTest = {
    name:string
    fly:()=>{}
}
// 如果pet有swim方法,那pet就是一个FishTest类型,
function isFish(pet: FishTest | BordTest): pet is FishTest{
    return (pet as FishTest)?.swim !== undefined
}
function getSmallPet(): FishTest | BordTest{
    let fish: FishTest = {
        name:'sharkey',
        swim: ()=>{return 0}
    }
    let bird: BordTest = {
        name:'sparrow',
        fly: ()=>{return 0}
    }
    return true ?bird : fish
}
let pet =getSmallPet()
console.log(isFish(pet))
if(isFish(pet)){
    pet.swim()
}else{
    pet.fly()
}

const zoo: Array<FishTest | BordTest> = [getSmallPet(),getSmallPet(),getSmallPet()]
const underWater:FishTest[] = zoo.filter(isFish)
const underWater2:FishTest[] = zoo.filter(isFish) as FishTest[]
console.log(underWater)
console.log(underWater2)
受歧视的unions:

问题:

interface Shape{
    kind:'circle' | 'square',
    radius?: number,
    sideLength?:number
}
function getArea(shape: Shape){
    return Math.PI * shape.radius ** 2 //无法判断shape是否是圆形
}

解决方案: 将shape拆分为两个类型,使得radius和sideLength都成为必须有的属性

interface Cicle{
    kind:'circle',
    radius: number
}
interface Square{
    kind:'square',
    sideLength: number
}
type  Shape = Cicle | Square
function getArea(shape: Shape){
    if(shape.kind === 'circle'){
        return Math.PI * shape.radius ** 2
    }
    return shape.sideLength ** 2
}
never类型与穷尽性检查

never: 不应该存在的状态

函数

函数类型表达式:

fn: (a:string) => void

type greatFunction = (a:string)=> void
function great(fn: greatFunction){
    fn('hello')
}
function printToConsol(str:string){
    console.log(str)
}
great(printToConsol)

调用签名

给函数定义签名:

type Description = {
    description: string, //定义属性
    (arg: number):boolean //参数是number类型,返回值是bool
}
function doSomething(fn: Description){
 console.log(fn.description + 'returned' + fn(6))
}
function fn1(n: number){
    return true
}
fn1.description = 'hello'
doSomething(fn1) //

构造签名

//js是没有函数签名的,因为其参数的可变性
class Ctor{
    s:string
    f:string
    constructor(s:string,f:string) {
        this.s = s
        this.f = f
    }
}
type SomeConstructor = {
   new (s:string,f:string):Ctor,
}
function fn2(ctor:SomeConstructor){
    return new ctor('hello', 'world')
}
const temp = fn2(Ctor)
console.log(temp.s)

//在同一个类型中结合调用和构造签名
interface CallOrConstructor{
    (n?: number):number
    new (s:string): Date
}
function fn(date: CallOrConstructor){
    let d = new date('2021-2-12')
    let e = date(100)
}

泛型函数

泛型: 两个值之间存在着对应关系,使用泛型去解决

作用: 使得输出的值和输入的值类型相同

//泛型
function firstElement(arr :any[]){
    return arr[0] //返回值的类型是由return 决定的
}
firstElement([1,22,3])
console.log(typeof firstElement([1,22,3])) //number

function secondElement<Type>(arr: Array<Type>):Type | undefined{
    return 100 //会报错,输入的是泛型,输出的也必须是泛型
}
secondElement([1,22,3]) 

function secondElement<Type>(arr: Array<Type>):Type | undefined{
    return arr[0]
}
secondElement([1,22,3]) //Type是number
//或者secondElement<number>([1,22,3])
console.log(typeof secondElement([1,22,3])) //number
secondElement(['1','22','3'])//Type是String
//或者secondElement<string>(['1','22','3'])
console.log(typeof secondElement(['1','22','3'])) //string
secondElement([true,false])//Type是boolean
//或者secondElement<boolean>([true,false])
console.log(typeof secondElement([true,false])) //boolean

//泛型的类型名称可以任意起
function map<Input,OutPut>(arr: Input[],fn: (arg: Input)=>OutPut): OutPut[]{
    return arr.map(fn)
}
const parsed  = map(['1','2','3'],(n)=>parseInt(n))
console.log(parsed) //[ 1, 2, 3 ]
泛型函数的限制条件
// Type extends {length: number}表示泛型type继承length属性,类型是number,
// extends {length: number}是一个限制条件,不是扩展条件
// 要求传入的参数必须有length属性
function longest<Type extends {length: number}>(a: Type,b:Type){
    return a.length>b.length ? a : b
}

const longArr = longest([1,2],[2,3,4])
const longStr = longest('sre','test')
// const longNum = longest(10,100)//会报错,此时Type是number,不具备length属性
使用受限值
function miniNumLength <Type extends {length: number}>
(obj:Type,minNum: number): Type{
    if(obj.length >= minNum){
        return obj
    }
    return {length: minNum} //要求返回泛型类型的代码,返回的是object类型不符合泛型
}
const arr = miniNumLength([1,2,3],6)
指定类型参数
function combine<Type>(arr1: Array<Type>,arr2:Array<Type>): Array<Type>{
    return arr1.concat(arr2)
}
combine([1,2,3],[2,3,4])
const arr = combine<string | number>([1,2,3],['2','3','4']) //手动规定参数类型
console.log(arr) //[ 1, 2, 3, '2', '3', '4' ]
编写优秀的通用函数的准则

1.了能的情况下,使用类型参数本身,而不是对其进行约束

2.尽可能少的使用类型参数

3.如果一个类型的参数只出现在一个地方,请重新考虑是否真的需要它

//1.了能的情况下,使用类型参数本身,而不是对其进行约束
function firstEle<Type>(arr: Array<Type>){
    return arr[0]
}
function firstEle1<Type extends any[]>(arr: Array<Type>){
    return arr[0] //使用类型约束去解析类型,而不是在调用时才解析,此时arr[0]返回的是any类型,不是number
}
const a = firstEle([1,2,3])
const b = firstEle1([1,2,3])

//2.尽可能少的使用类型参数
function filter1<Type>(arr: Type[], fn:(arg:Type)=>boolean){
    return arr.filter(fn)
} //这种方式更好
function filter2<Type,Func extends (arg: Type)=>boolean>(
    arr:Type[],
    func: Func
){
    return arr.filter(func)
}

//如果一个类型的参数只出现在一个地方,请重新考虑是否真的需要它
function great <Type extends string>(s:string){
    return 'hello' + '' + s
}
function great2 (s:string): string{
    return 'hello' + '' + s
}//这种方式更好

函数的可选参数

在写回调函数时,永远不要写一个可选参数,除非打算在不传递参数的情况下调用函数

function f(n?:number){ //n是可选的
    console.log(n?.toFixed())
    console.log(n?.toFixed(3))
}
function f1(n:number = 100){ //给设置默认值
    console.log(n?.toFixed())
    console.log(n?.toFixed(3))
}
f(120.45)
f()

function myForEach(arr: any[], callback:(arg:any,index?:number)=>void){
    for(let i = 0; i <arr.length; i++){
        callback(arr[i])
    }
}
myForEach([1,2,34],(a)=>console.log(a))
myForEach([1,2,34],(a,i)=>console.log(a,i))//此时的i可能是undefined 也有可能是number
myForEach([1,2,34],(a)=>console.log(a))

函数重载

根据函数的参数类型和个数去调用方法

function makeDate(timeStamp:number) :Date //重载函数
function makeDate(m:number,d:number,y:number) :Date //重载函数
//实现函数的参数是为了兼容重载函数,不能根据实现参数的个数去要求重载函数
function makeDate(mOrTimeStamp:number,d?:number,y?:number):Date{ //实现函数
    if(d&&y){
        return new Date(y,mOrTimeStamp,d)
    }else{
        return new Date(mOrTimeStamp)
    }
}
const ab = makeDate(123)
const b = makeDate(1,4,3)
// makeDate(5,9) //会报错: 因为没有需要两个参数的函数重载
console.log(ab)//1970-01-01T00:00:00.123Z

console.log(b)//1903-02-03T16:00:00.000Z
重载签名和实现签名
function fn(x:string):void;
function fn(){
    
} 
// fn() //会报错  外部调用时,只能观察到重载函数的参数,观察不到实现函数的参数个数 
fn('hello')

function fn11(x:boolean):void;
function fn11(x:string):void;
function fn11(x:boolean | string){

} //实现函数签名的参数列表要和重载函数参数列表相呼应


function fn11(x:boolean):string;
function fn11(x:string):boolean;
function fn11(x:boolean | string):string|boolean{
    return true
} //实现函数的返回值和重载函数的返回值类型相呼应

编写好的重载

在可能的情况下总是倾向于使用联合类型的参数而不是重载参数

function len(s:string):number;
function len(arr:any[]):number;
function len(x: any){
    return x.length
}
len('hello')
len([1,2,3])
len(Math.random() > 0.5 ? [1,2,3] : 'hello') //会报错 会返回一个联合类型string | number[]

//完全可以不用写重载
function test(x:string | Array<any>){
    return x.length
}

函数内的this声明

interface User{
    admin: boolean
}
interface DB{
    filterUsers(filter:(this: User)=>boolean): Array<User>
    //filterUser方法返回一个User类型的数组,接受一个function类型的参数filter
    //filter函数接受一个类型为User的this参数,返回值是boolean
}
const db:DB = {
   filterUsers:(filter: (this: User) => boolean) =>  {
       let user1: User = {
           admin:true
       }
        let user2: User = {
            admin:false
        }
        return [user1,user2]
   }
}
const admins = db.filterUsers(function (this: User){
    console.log(this)
    return this.admin //此时this指向的是User,this可以指代传入的参数
})
console.log(admins)
// const admins = db.filterUsers((this: User) => { //会报错,箭头函数内部不能定义this
//     console.log(this)
//     return this.admin //此时this指向的是User
// })

其他类型

void: 没有返回值的函数的返回值,没有返回值或者返回值语句中没有返回明确的值

TS中,undefined和void是不一样的

object: 任何不是任何基元类型(string,number,bigint,boolean,symbol,null,undefined)的值,不同于{}。也不同于Object,始终使用obbject

unknown: 代表任何值,与any类似,但更安全,因为对unknown值做任何事都不合法

never: 永远不会被观察到的值

Function: 描述了诸如bind,call,apply和其他类型存在于javascript中所有函数值的属性,它还有一个特殊属性: Function类型的值总是可以被调用,这些调用返 回any

参数展开运算符——形参展开

function multiply(n:number,...arr: number[]){ //...arr: 表示不定长度的数字数组
    return arr.map(item => item * n)
}
const aa = multiply(10,23,34,56,23,23)
console.log({aa}) //{ aa: [ 230, 340, 560, 230, 230 ] }

参数展开运算符——实参展开

function reaMultiply(arr: Array<number>){
    console.log(...arr)//1 2 3 4
}
reaMultiply([1,2,3,4])

const args = [8,5] //类型推断为: 长度为0或者长度不定的数值型数组
const arsgh = Math.atan(...args) // 而Math.atan需要的是两个参数,所以会报错

//修改
const args2 = [6,5] as const
const angle = Math.atan2(...args2)

参数结构

采用参数重构的方法将参数解压到函数主体或多个局部变量中

function  sum({a,b,c}:{a:number,b:number,c:number}){
    console.log(a+b+c)
}
sum({a:10,b:3,c:9})

//简化一下:
type ABC = {
    a:number,
    b:number,
    c:number
}
//function  sum({a,b,c}:ABC)

函数返回的void类型

一个具有void返回值类型的上下文函数类型(type vf = () => void),在实现时,可以返回任何其他的值,但会被忽略

当一个函数字面的定义有一个void的返回类型时,该函数必须不返回任何东西

type VoidFunc = () => void

const f11: VoidFunc = ()=> {return true}
const f2: VoidFunc = ()=> true
const f3: VoidFunc = function (){
    return true
}
const f4: VoidFunc = function (): void{
    return true //会报错,显式定义返回值为void之后将不允许返回任何值
}
const v1:boolean = f11() // 会报错 不能将boolean分配给void
const v2 = f2()
const v3 = f3()

对象类型

匿名对象/接口命名/类型别名

//第一种写法 匿名对象
function great(person: {
    name:string,
    age:number
}){
    return 'hello' + person.name
}

//第二种: 接口定义
interface Person{
    name:string
    age: Number
}
function greater(person: Person){
    return 'hello' + person.name
}

//类型别名
type Person = {
    name:string,
    age:number
}
function greated(person: Person){
    return 'hello' + person.name
}

属性修改器

属性: 定义对象类型/设置属性是否可选/设置属性是否可写

可选属性
interface PaintOptions{
    shape: Shape,
    x?:number, //x是可选的
    y?:number //y是可选的
}
type Shape = {

}
function paintShape({shape: Shape,x = 0,y = 0}: PaintOptions){
    // {shape:Shape,x:number = 0,y:number = 0}在函数中,此时真的number和Shape是x,y和shape的别名
    //在结构的时候不要定义类型
    console.log(x,y)
    console.log(Shape)
}
const shape:Shape = {}
paintShape({shape}) //shape是必填的
paintShape({shape,x:22})
paintShape({shape,x:22,y:22})
只读属性

在被标记为只读的属性,类型检查期间不允许写入其他的值

将一个对象本身设置为readOnly,那么不允许对这个对象本身进行赋值操作,但是其属性是可以修改的

将对象的属性设置为readOnly之后,属性的值也将不可修改

interface SomeType{
    readonly prop: string //表示prop是只读属性
}
function doSomethig(obj: SomeType){
    // obj.prop='test' //会报错,因为prop是个只读属性
}
interface Home{
    readonly resident:{
        name:string,
        age:number
    }
}
function visit(home: Home){
    console.log(home.resident.name)
    home.resident.name = 'temp'
    home.resident.age = 12
}
function evict(home:Home){
    // home.resident = {
    //     name: 'aa',
    //     age:18
    // } 会报错 因为resident本身是不可写的
}

//typeScript在类型赋值时并不会去校验它是不是可写的
interface ReadOnlyPerson{
    readonly name: string
    readonly age1: number
}
let writablePerson: Person1 = {
    name:'test',
    age1:18
}
// let readOnlyPerson: ReadOnlyPerson = {
//     name:'aa',
//     age1:16
// }
let readOnlyPerson: ReadOnlyPerson = writablePerson
console.log(readOnlyPerson.age1) // 18
writablePerson.age1++
console.log(readOnlyPerson.age1) // 19
索引签名

是指属性名和值的可能值的类型,任意的给一个对象定义任何类型的属性,丰富对象的属性类型

interface StringArray{
    [index: number]:string//对象StringArray属性的名字是number类型,属性值的类型是string类型
}

const myArray: StringArray = ['a','b']
const secondItem = myArray[0]

interface testString{
    [prop: string]: number
}
const testString: testString = {
    'name': 12,
    'age':18,
    temp: 300
}

interface Animal{
    name: string
}
interface Dog extends Animal{
    bread:string
}
interface NotOkay{
    [index: string]: number | string
    length: number,
    name: string
}
const notOkay: NotOkay = {
    x:100,
    length:12,
    name: 'hello'
}
interface ReadOnlyString{
    readonly  [index: string]: number
}
const temp: ReadOnlyString = {
    age:18
}
temp.age = 15 // 会报错,因为age是不可读的

扩展类型

使用extends扩展

interface BasicAddress{
    name?: string
    street?:string
    city:string
    county:string
    postalCode:string
}
//向接口BasicAddress中添加unit,用extends将BasicAddress扩展
interface AddressWithUnit extends BasicAddress{
    unit: string
}
interface Colorful{
    color:string
}
interface Circle{
    radius: number
}

//ColorCircle继承Circle和Colorful
interface ColorCircle extends Colorful,Circle{

}
const cc:ColorCircle = {
    color:'red',
    radius:100
}

交叉类型

用于组合现有的对象类型的

interface Colorful{
    color: string
}
interface Circle{
    radius: number
}
type CircleColor = Colorful & Circle //使得CircleColor具有Circle和Color的所有属性

function draw(circle: Colorful &  Circle){
    console.log(circle.radius)
    console.log(circle.color)
}
draw({
    color:'red',
    radius: 100
})

接口和交叉类型的区别

接口使用extends来扩展,交叉类型采用&来扩展

处理冲突: 同名的接口存在时,interface会合并接口,而type不允许有重复的类型,因此interface适用于合并多个类型,type适用于避免重复类型的场景

interface Sister{
    name: string
}
interface Sister {
    age: number
}
const sister:Sister = {
    name:'test',
    age:16
}
// type Sister = {
//
// }
// type Sister = {
//
// } //会报错

泛型对象类型

interface Box <Type>{
    content: Type
}
const box: Box<string> = {
    content:'test'
}
const box1: Box<number> = {
    content:12
}

interface Apple{
    name:string
}
type AppleBox = Box<Apple>
let ab:AppleBox = {
   content:{
       name:'232'
   }
}

type Box1<Type> = {
    contents: Type
}
type OrNull<Type> = Type | null //OrNull可以是Type也可以是null
type OneOrMany<Type> = Type | Type[]
type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>> // OneOrManyOrNull的类型可以是Type,Type[],null
type OneOrManyOrNullOrString = OneOrManyOrNull<string>

类型操纵

从类型中创建类型:

1.泛型类型 2.keyof 类型操作符 3.typeof 类型操作符 4.索引访问类型 6.条件类型 7.映射类型 8.模板字面量类型

泛型类型
function identityTest<Type>(arg: Type): Type{
  return arg
}

let myIdent: <Type>(arg: Type)=>Type = identityTest //定义泛型函数的方式定义myIdent的类型
let myIdent1: <Input>(arg: Input)=>Input = identityTest //泛型的名字不一定非得用type

let test1:{<Type>(arg: Type): Type} = identityTest  //用字面量的形式定义泛型

interface GenerqatorIdentity {
    <Type>(arg: Type) : Type
} //泛型接口
let myTest:GenerqatorIdentity = identityTest

interface GenericTest<Type>{
    (arg: Type): Type
}
// let myTest1:GenericTest<= identityTest //会报错 因为没有输入泛型的确定类型
let myTest1:GenericTest<string> = identityTest //用户需要输入类型

泛型类

泛型类与泛型接口类似:

class GenericNum<NumType>{
    zeroValue: NumType
    add:(x: NumType, y: NumType)=> NumType
}
const myGenirc = new GenericNum<number>() // <number>是自定义的
myGenirc.zeroValue = 0
myGenirc.add = function (x,y): number{
    return x + y
}
console.log(myGenirc.add(1,2))
泛型约束
function test<Type extends LengthLimit>(arg: Type): Type{ 
    //Type extends LengthLimit 表示传入的参数必须要有length属性
    return arg
}
interface LengthLimit{
    length: number
}
// test(3)//报错
test('2') //string具有length属性
在泛型约束中使用类型参数

生成一个受其他类型约束的类

function getProperty<Type,Key extends keyof Type>(obj: Type, key: Key){
//    key一定属于Type类型的某个key
    return obj[key] //返回对象的value值

}
let x = {
    a:1,
    b:2,
    c:3
}
getProperty(x,'a') // 1
// getProperty(x,'m') // 会报错 x对象中没有m
在泛型中使用类类型
function create<Type>(c: { new(): Type}):Type{
    return new c()
}
class FeBeeKeeper{
    hasMask: boolean = true
}
class ZooKeeper{
    namestr: string = 'me'
}
class Animal{
    numLegs: number = 4
}
class Bee extends Animal{
    keeper: FeBeeKeeper = new FeBeeKeeper()
}
class Lion extends Animal{
    keeper: ZooKeeper = new ZooKeeper()
}
function createInstance<A extends Animal>(c: new() => A): A{ //<A extends Animal>表示A必须继承Animal
    return new c()
}
const myLion = createInstance(Lion).keeper.namestr
const myBee = createInstance(Bee).keeper.hasMask

keyof 类型操作符

type Point= {
    x: number
    y:number
}
type P = keyof Point //将Point类型的key作为联合类型付给P:即: 'x'| 'y'

const p1: P = 'x'
const p2: P = 'y'
// const p3: P = 'z' //报错,Point中没有key为z的键值对

type Arrayish = {
    [n: number]:unknown
}
type A = keyof Arrayish
const a:A = 0

type Mapish = {
    [k:string]:boolean
}
type M = keyof Mapish //M是number | string
const m: M = 1
const m1: M = 'hello'

typeof类型操作符

typeof返回一个类型

const v: number = 1
let s : typeof v //typeof v是number,将number付给S的类型
s = 1
s=3
// s= 'hello' //会报错 s是number类型的变量

//ReturnType<T> 预定义: 要求必须传入一个函数类型
type Predict = (x: unknown) => boolean
type K = ReturnType<Predict>
function f(){
    return{
        x:10,
        y:10
    }
}
type Q = ReturnType<typeof f> //不能用typeof标识函数处理的结果
// const q: Q = 100

function MessageBox(){}
let shouldCon: typeof MessageBox
// shouldCon = 100 //会报错 shouldCon是一个()=>void函数类型
//不能写成如下方式: typeof MessageBox()只能修饰变量或者某个对象的某个属性

索引访问类型

通过对象的属性获取值的类型:

当我们使用typeof运算符来获取变量的类型时,对于字符串字面量类型的变量,TypeScript会将其解析对应成对应的字符串字面量类型

interface Person{
    name:string
    age: number
    address: string
}

type Temp = Person['name'] //Type是string类型
const ss:Temp = 'a'
// const aa:Temp = 10 //会报错 aa是string类型的

interface Male{
    detail: Person['name' | 'age'] //detail的类型是string | number
}
type Female = Male['detail']
const ff :Female = 'TEST'
const GG :Female = 20
// const LL :Female = true 会报错

type K2 = Person[keyof Person] // K2的类型是:string | number

type K3 = 'name' | 'age'
type I3 = Person[K3] //I3的类型是string | number
const rr: I3 = ''
const rw: I3 = 0
// const rg: I3 = true //会报错,因为rg是string | number

//在索引的时候只能使用类型
type Age = typeof myArr[number]['age'] // Age是number类型

const key = 'age'
type Key = 'age'
// type A4 = Person[key] 会报错,key表示的是值,不是类型
type A4 = Person[typeof key] //此时A4是number类型的,typeof key被解析成了'age',而不仅仅是一个字符串值
type A5 = Person[Key] //此时key是age类型的

const aa: A4 = 7 
// const ab: A4 = ''会报错

条件类型

interface Animal {
    live: ()=> void
}
interface Dog extends Animal{
    woof: ()=>void
}
//Example是number
type Example = Dog extends Animal ? number : string //Dog是否继承自Animal,如果是Example是number类型的反之string

//Example2是string类型
type Example2 = RegExp extends Animal ? number : string

interface IdLabel {
    id:number
}
interface nameLabel{
    name: string
}
//函数重载的写法
function createLabel(id: number):IdLabel
function createLabel(name: string): nameLabel
function createLabel(nameOrId : number | string): IdLabel | nameLabel
function createLabel(nameOrId : number | string): IdLabel | nameLabel{
    return {
        id:123
    }
}
//利用条件类型的写法
type NameOrId <T extends number | string> = T extends number ? IdLabel :nameLabel

function created<Type extends number | string>(idOrName: Type): NameOrId<Type>{
    throw '' // 抛出错误不在执行
}
//nameLabel
let abd =created('typescript')
//idLabel
let dd =created(12)
//nameLabel | idLabel
let cc = created(Math.random() > 0.5 ? 'hello' : 23)

条件类型约束

type MsgOf<T extends {‘message’: unknown}> = T[‘message’] // 表示MsgOf必须有message属性
interface EmailCnt{
message:string
}
type EmailMsgCnt = MsgOf

type MsgOf1 = T extends {message: unknown} ? T[‘message’] : never

interface Dog{
name:string
bark:()=>void
}
const emailCnt: EmailMsgCnt = ‘123’
console.log({emailCnt})

type DogMsgCnt = MsgOf1 //never类型
// const dmsg: DogMsgCnt = ‘HELLO’ //会报错

在条件类型内进行推理

infer关键字:,infer 关键字可以用来从类型中推断出新的类型

type GetReturnType<Type> = Type extends (...args: never)=> infer Return ? Return : never

// infer:关键字在 TypeScript 中,infer 关键字可以用来从类型中推断出新的类型。
// 当你在一个接口(interface)中定义了一个 infer 字段时,它会在接口中添加一个 readonly 的属性,这个属性不会被允许被访问。
// 然后,你可以使用 infer 关键字来从接口中推断出新的类型。
// 例如,如果你定义了一个接口 MyInterface,其中有一个 string 类型的字段 myProperty,
// 你可以使用 infer 关键字来推断出一个新的类型 MyProperty<string>。这个新的类型可以在接口中使用,
// 并且不会对原始的 MyProperty 字段产生影响。

//Num1的类型是number
type Num1 = GetReturnType<() => number>
const n1: Num1 = 12
type Str1 = GetReturnType<string>
const s1 = ''

分布式条件类型

当条件类型作用于通用条件类型时,我们给定一个联合类型,就变成了分布式条件类型

type TwoArray<Type> = Type extends any ? Type[] : never
//分布式
//SerArrOrNumArr的类型是: string[] | number[]
type SerArrOrNumArr = TwoArray<string | number>

let sa:SerArrOrNumArr = [1,2,3]
// let s11:SerArrOrNumArr = 'a' 会报错
let saa:SerArrOrNumArr = ['e']

//非分布式
type ToArrayNoneDis<Type> = [Type] extends [any] ? Type[] : never
//strOrNumArr的类型是(string | number)[]
type strOrNumArr = ToArrayNoneDis<string | number>

类成员:类属性,readonly,构造器,方法,Getters/Setters,索引签名

//第一种
class Point{
    x: number = 0
    y: number = 0
}
const pt = new Point()
pt.x = 1
pt.y = 2
console.log(pt.x,pt.y) // 1,2
//第二种
class Point{
    x: number = 0
    y: number = 0
    constructor(x:number,y:number) {
        this.x = x
        this.y = y
    }
}
const pt = new Point(1,2)
console.log(pt.x,pt.y)

class Okay{
    name!:string //当name未进行初始化时 可以使用断言操作符
}

readonly:防止在构造函数之外更改属性的值

readonly:防止在构造函数之外更改属性的值

class Genator{
    readonly name:string = 'w'
    constructor() {
        this.name = 'hello'//加了readonly的属性在构造器中可以二次更改它的值
    }
    err(){
        // this.name = 'err'//会报错, 函数体里也不能更改readonly的属性
    }
}
const ge = new Genator()
console.log(ge.name)
// ge.name = 'yh' //会报错,name是只读属性

// 直接初始化属性和用construcor初始化的区别:
//     construcor严格意义上来说是由用户进行的初始化,而直接初始化是在定义的时候进行
//     constructor是在初始化实例的时候运行,即用new操作符调用的时候,可以传入参数

构造器: constructor

构造函数不能有类型参数,构造函数不能有返回类型注释

class Point1{
    x
    y
    constructor(x: string | number = 0,y:string | number = 0) {
        this.x = x
        this.y = y
    }
}
const p1 = new Point1(1,2)
const p2 = new Point1()

class Base{
    key=4
}
class Derived extends Base{
    constructor() {
        super();
        console.log(this.key)
    }
}

方法: 类的函数属性

class Point2{
    x:number = 10
    y: number = 10
    scale(n: number): void{
        this.x = n * this.x
        this.y = n * this.y
    }
}
const p3 = new Point2()
p3.scale(10)
console.log(p3.x,p3.y)//100 100

访问器: Getters/Setters

如果类里面的属性存在get,但没有set ,则该属性是只读的

如果没有指定setter参数的类型,那将从getter的返回类型中推断出来

访问器和设置器必须有相同的成员可见性

class C1{
    _length = 0
    get length(){
        return this._length
    }
    set length(value){
        this._length = value
    }
}
let c:C1 = new C1()
// c.length() 会报错 get访问器不能调用
console.log(c.length) //0 
c.length = 100
console.log(c.length) // 100

class C2{
    _size = 0
    get  size():number{
        return this._size
    }
    //4.3以后支持
    set size(value:string|number){
        let num = Number(value)
        if(!Number.isFinite(num)){ //判断num是不是有穷数字
            this._size = 0
        }
         this._size =num //value的值类型一定是get的返回类型
    }
}
let t : C2 = new C2()
t.size = 'hello'
console.log(t.size) //NAN
t.size = 12
console.log(t) //12

继承

implements子句

在TypeScript中,implements 是一种关键字,用于定义一个接口(interface),描述了一组方法的集合,这些方法必须实现该接口。
implements 通常用于以下情况:

在TypeScript中,implements 是一种关键字,用于定义一个接口(interface),描述了一组方法的集合,这些方法必须实现该接口。
implements 通常用于以下情况:
\1. 定义一个接口,但是不想在当前项目中使用它,或者不想让其他开发者使用它。
\2. 需要定义一个接口,但是该接口依赖于其他接口或者类,而自己并不需要实现这些接口或类。
使用 implements 关键字可以有效地定义一个接口,使得其他开发者可以在自己的项目中使用该接口,而自己项目中的开发者则不需要了解该接口的具体实现细节。
例如,可以在一个 TypeScript 文件中使用 implements 关键字来定义一个接口,然后在该接口中声明一些方法,将这些方法标记为 implements 修饰的接口的方法,从而使该接口具有了行为。其他开发者可以在自己的项目中使用该接口,并通过实现该接口来调用接口中声明的方法。

interface C{
    ping(): boolean
    point?: boolean // 定义可选属性之后 在类里面可写可不写
}
class Middle implements C{ // 在C里面要重写ping方法,因为使用了implements关键字
    isShow: boolean = true
    ping(): boolean {
        return this.isShow
    }
}
const m = new Middle()
console.log(m.ping()) // true
extends子句

​ 实现一个类继承另一个类,被继承的类叫做基类,继承的类叫做子类,子类拥有父类的所有属性和方法

class Animal{
    move(){
        console.log('moving')
    }
}
class Dog extends Animal{
    woof(times: number){
        for(let i=0; i<times;i++){
            console.log(i)
        }
    }
}
const d = new Dog()
d.move()
d.woof(4)
重写方法

TypeScript强制要求派生类总是它基类的一个子类型

//要重写父类的方法,需要保证派生类的参数和父类的兼容

派生类是指在 JavaScript 中,派生类(Subclass)是指一个类(Class)可以继承另一个类的属性和方法,从而扩展或复用其功能。派生类是一种实现继承的方式,其中一个类可以继承另一个类的属性和方法,从而创建一个新的类。派生类可以继承父类的构造函数、方法、属性等等,从而实现代码的重用和复用。

class BaseClass{
    greet(){
        console.log('base')
    }
}
class DerivedClass extends BaseClass{
    greet(name?: string) { //要重写父类的方法,需要保证派生类的参数和父类的兼容
       if (typeof name === 'undefined'){
           super.greet()
       }else{
           console.log(name?.toUpperCase())
       }
    }
}
const d1 = new DerivedClass()
d1.greet() // base 在这里greet调用的是派生类的方法,然后派生类再去调用基类的方法
d1.greet('temp') //TEMP

const b:BaseClass = d1 //派生类是基类的一个子类
b.greet() //base
基类和派生类的初始化顺序

顺序:

// 基类的字段被初始化
// 基类构造函数运行
// 派生类的字段被初始化
// 派生类的构造函数运行
class Base1 {
    name:string = 'base'
    constructor() {
        console.log(this.name + 'baseName')
    }
}
class DeClass extends Base1{
    name:string = 'Derived'
    constructor() {
        super();
        console.log(this.name)
    }
}
const d2 = new DeClass() // 输出结果: basebaseName  Derived
继承内置类型

内置类型: Array,Map,Err等

class MsgError extends Error{
    constructor(m: string) {
        super(m);
        //明确的设置原型
        // Object.setPrototypeOf(this,MsgError.prototype)
    }
    sayHello(){
        return 'hello' + this.message //不报错是因为Error类里面有一个message
    }
}
const e = new MsgError('hello')
console.log(e.sayHello()) //hellohello

const msg = new MsgError('hello')
console.log(msg instanceof MsgError) //true

类成员的可见性

public: 公开的默认值,任何对象在任何地方都可以访问 --当前类,当前类的派生类,当前类的派生类的实例都可以访问

protected: 受保护的,只能在当前类或子类中进行访问–派生类可以暴露基类受保护的成员

private: 私有的,只能在当前类中进行访问

class Greeter{
    public greet(){
        console.log('hello')
    }
    public sayHello(){
        console.log(this.greet())
    }
}
const g = new Greeter()
g.greet()
g.sayHello()
class Hello extends Greeter{
    constructor() {
        super();
        this.greet()
    }
}
const h = new Hello()
h.greet()

class Greeter{
    protected age = 12
    private address = 'test'
    public greet(){
        this.getName()
    }
    protected getName(){
        console.log('hello')
    }
    public getAddress(){
        console.log(this.address)
    }
}
class SpecialGreeter extends Greeter{
    public age = 18
    constructor() {
        super();
        // console.log(this.address)// 会报错,因为address是private修饰的
    }
    superHoby(){
        this.getName() //’hello‘ ,子类中是可以访问的
    }
}
const s = new SpecialGreeter()
// s.getName() //会报错  实例访问基类protected修饰的方法时会报错
console.log(s.age) // 18 派生类可以修改基类的可访问性
// console.log(s.address) 会报错

//跨实例访问私有属性
class A{
    private x: number = 10
    public sameAs(other: A){
        return other.x === this.x // 在类里面可以访问其他类的私有属性
    }
}

类的静态成员

在TypeScript中,类的静态成员(static members)是指在类定义中使用关键字static定义的成员,只能在类内部访问,不能被实例化对象的外部访问。
静态成员可以分为两种类型:
静态字段(static fields):定义在类上的字段,只能被类本身访问,不能被实例化对象的外部访问

​ 静态方法(static methods):定义在类上的方法,只能被类本身访问,不能被实例化对象的外部访问。

// 不与类的特定实例相关联,可以通过类的构造函数对象本身去访问
// 访问静态成员不需要实例化 直接用类的名字和属性访问符就可以
// 特殊的静态成员名称不安全,避免使用name,length,call等
// TS没有静态类的概念,因为有函数和普通对象
// 静态成员也可以用private,protected,public修饰
// 静态成员是会被继承的
class MyClass{
    private static  x = 0
    static printX(){
      console.log(MyClass.x)
    }
}
MyClass.printX() // 0
// console.log(MyClass.x) 会报错 因为在x是一个私有的静态成员,外部不能访问

class Base2{
    static getGreet(){
        return 'hello, world'
    }
}
class Dev2 extends Base2{
    myGreet = Dev2.getGreet() // 静态成员是会被继承的
    
}

类里的static区块

class Foo{
    static #count = 0 //#表示这个属性是私有的
    get count(){
        return Foo.#count
    }
    static {
        try{
            const lastInstance = {
                length: 100
            }
            Foo.#count = lastInstance.length
        }
        catch {
            
        }
    }
    
}
// Foo.count 会报错 count是内专用

类运行时中的this

ts只是在开发时做一些类型检查,并不会影像JS运行时的行为

使用箭头函数,会浪费更多的内存,因为每个类实例都将有自己的副本,并且不能再派生类中调用super获得父类的方法,因为再原型链中没有入口可以获得基类的方法

​ 使用this参数,每个类定义只有一个函数被分配,而不是每个实例都会创建一个函数,基类的方法仍然可以通过super来调用

class MyClass1{
    name = 'myclass'
    // getName(){
    //     return this.name
    // }
    // getName = () => this.name
    getName(this: MyClass1){ //该方法接收一个参数 this,表示当前对象(即 MyClass1 类的实例),并不会影像结果。
        return this.name
    }
}
const c1 = new MyClass1()
console.log(c1.getName()) // myclass

const obj = {
    name: 'obj',
    getName: c1.getName
}
console.log(obj.getName()) // obj

//想让getName一直返回当前类的名称,可以将getName定义成箭头函数
// 或者可以用this当作参数

this类型

this类型

// this会动态指向当前类的类型
class Box{
    content: string = ''
    set(value: string){
        this.content = value
        return this
    }
}
class ClearableBox extends Box{
    clear(){
        this.content = ''
    }
}
const a = new ClearableBox()
const f = a.set('hello')
console.log(f) // this指向的是ClearableBox { content: 'hello' }

基于类型守卫的this: this is Type

结合if判断使用,缩小类型范围

class FileSystemObject{
    isFile(): this is FileRep{
        return this instanceof FileRep
    }
    isDirectory(): this is Directory{
        return this instanceof Directory
    }
    isNetworked(): this is Networked & this{
        return this.networked
    }
    constructor(public path: string,private networked: boolean) {

    }
}
class FileRep extends FileSystemObject{
    constructor(path: string, public content: string) {
        super(path,false);
    }
}
class Directory extends FileSystemObject{
    children: Array<FileSystemObject>
    constructor() {
        super('',false);
        this.children = []
    }
}
interface Networked{
    host:string
}
const fso: FileSystemObject = new FileRep('./bar/test','foo')
if(fso.isFile()){
    //const fso: FileRep
    console.log(fso.content)
}else if(fso.isDirectory()){
    //const Directory: FileRep
    console.log(fso.children)
}else if (fso.isNetworked()){
    //const  isNetworked
    console.log(fso.host)
}

// BoxTemp是一个泛型类,它具有一个可选的value属性来存储任意类型的值。
// hasValue()是一个方法,它返回一个布尔值,指示BoxTemp实例是否具有非undefined的value属性。
// 如果value不是undefined,则说明实例具有值,返回true;否则,返回false。
// this is {value: T}是一个类型谓词,用于在返回值类型中指定更具体的类型信息。
// 它表示如果类型推断使用此方法后返回true,那么该实例将被视为具有特定类型{value: T}。
// 简而言之,BoxTemp类允许创建可以包含任何类型的值的实例,并且提供了一种检查实例是否具有值的方法。
class BoxTemp<T>{
    value?: T
    hasValue(): this is {value: T}{
        return this.value !== undefined
    }
}

const box1 = new BoxTemp()
box1.value = 'tets'
if(box1.hasValue()){
    console.log(box1.value)
}

参数属性

在类的构造函数中,可以结合修饰符定义参数,这些定义的参数既是构造函数的参数又是类的成员,而且具有类的修饰符的属性

class Params{
    constructor(public readonly x: number, protected y: number,private z: number) {
    }
}
const p: Params = new Params(12, 13, 14)
console.log(p.x)
console.log(p) //Params { x: 12, y: 13, z: 14 }

// p.x= 15 会报错,因为x是只读属性
// console.log(p.y) 会报错 因为protected修饰的属性是不能在实例访问的
// console.log(p.z) 回报错 因为private定义的属性是私有的,不能在实例访问

类表达式

类表达式不需要名字,类似于匿名类

const SomeClass = class <Type>{
    content:Type
    constructor(value: Type){
        this.content = value
    }
}
const h = new SomeClass(12)
console.log(h.content) // 12

抽象类和成员

​ 在TypeScript中,抽象类(Abstract Class)是一种特殊的类,用于定义其他类的基础。抽象类可以包含普通类(通常被称为抽象类)和静态成员(通常被称为静态成员)。
​ 普通类可以包含任意数量的成员,包括抽象方法和普通成员。抽象方法是只能在子类中实现的成员,它们必须使用关键字abstract来定义。普通成员则是指可以在子类中继承的成员,它们没有特殊的访问权限。====
抽象类还可以包含一个构造函数和一个析构函数。构造函数用于初始化对象,而析构函数则用于在对象被销毁时进行清理工作。
​ 在TypeScript中,抽象类和成员的概念对于设计类和接口非常重要。通过使用抽象类和成员,可以确保子类具有相同的接口,并且可以实现抽象方法。这有助于在项目中实现统一、可维护的接口,并促进代码的可读性。

abstract class BaseMiddle{
    abstract getName():string //定义抽象方法的时候不用实现,意义在于让别人去实现
    printName(){
        console.log(this.getName())
    }
}
// const b = new BaseMiddle() 抽象类不可以实例化
class DerivedBase extends BaseMiddle{
    getName(): string {
        return "world";
    }
}
const b1 = new DerivedBase()
console.log(b1.getName()) // world
function greet(ctor: new () =>  BaseMiddle){
    //构造签名
    const instance = new ctor()
    instance.printName()
}
greet(DerivedBase)

类之间的关系

类可以相互替代,当两个类的成员属性和类型是一致的,即使值不同,也可以互相替代

空的类相当于一个普通的函数,传入任何值都是可以的

class Point11{
    x = 0
    y = 0
}
class Point22{
    x = 0
    y = 1
}
const point : Point11 = new Point22() //类可以相互替代,当两个类的成员属性和类型是一致的,即使值不同,也可以互相替代

class Person{
    name:string = ''
    age:number = 12
}
class Employee{
    name:string='ss'
    age:number = 12
    salary: number = 10
}
const per: Person = new Employee() // TS内部做了类的类型判断

class Empty{

}
function fn(x: Empty){

}
fn(window)
fn({})
fn(()=> {})
fn(12)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值