TypeScript笔记

文章目录

    • 什么是TS
    • 前期准备
      • 安装TS
      • TS配置文件
    • 基础类型
      • 原始类型
      • object类型
      • 数组类型
      • 元组类型
      • 枚举
      • 函数类型
        • 可选参数和默认参数
        • 剩余参数
      • any任意类型
    • 高级类型
      • 交叉类型
      • 联合类型
    • 接口
    • 泛型
    • 类型别名
    • 参考

什么是TS

官网介绍:TypeScript是JavaScript类型的超集,它可以编译成纯JavaScript。可以在任何浏览器、计算机、操作系统上运行,并且是开源的。 最终会编译成JavaScript语法在浏览器中运行。

在这里插入图片描述

类型系统,从可从两个角度来考虑

  • 类型安全:可分强类型弱类型语言,

    • 强类型:在语言层面上,就约束着函数的实参类型必须与形参类型相同,另外不允许隐式类型转换。
    • 弱类型:在语言层面上,不要求函数的实参类型与形参类型是相同的,允许隐式类型转换。
  • 类型检查:可分静态类型动态类型语言

    • 静态类型:一个变量声明时它的类型就是明确的(编译阶段),中间过程是不允许再修改变量类型了,其中java就是典型的静态类型语言。
    • 动态类型:一个变量在允许阶段才能明确变量的类型,而且可以随意修改变量的类型,其中js就是典型的动态类型语言。
// 强类型 kotlin
fun sum(a:Int,b: Int){return a + b
}
// 在调用时,函数实参类型必须形参是一样的,
sum(1,2) // 对
sum("1",2) // 错  在调用时就直接报错了,不允许隐式类型转换,类型不匹配
var c = 2 // 声明时Int型
c ="2" // 错 ,不允许修改变量类型// 弱类型 js
funtion sum2(a ,b){return a + b
}
// 在调用时,函数实参类型可以形参是不一样的
sum(1,2) // 对
sum("1",2) // 虽然调用时不会报错  但和我们预期是不一样的 输出12 ,是字符串拼接结果 let d = 3
d = "3"  // 对 允许修改变量类型

从类型系统角度看,JavaScript是一个弱类型且动态类型语言,灵活度是相当的高,正因为这种灵活多变的任性 ,从而丢失了类型系统的安全可靠性,在编码时注意要类型的安全性,如果在开发环境没有及时发现,可能在生产环境导致程序的异常。

JavaScript是没有编译阶段,可以直接运行,因此具有灵活多变的特性,也带来很大的缺陷,比如:

  • 类型异常只有在运行时才可以发现。
  • 类型不明确导致的结果不符合预期。
// 类型异常只有在运行阶段才会被发发现
let obj = {}
console.log(obj.name) // undefined// 类型不明确导致的结果不符合预期
function sum(a, b) {return a + b
}
console.log(sum("1", 2)) // 输出12 预期是3

而TS正好可以弥补JavaScript这种任性,在编码时能够确定类型的一致性,然后还可以保持着JavaScript的灵活度。强类型的优势:

  • 类型安全,在编码时,能确保类型的一致性,让错误在编译时更早的暴露。
  • 代码编写更智能,更准确,提示更友好,只会提示对象存在的属性或函数。
  • 可以减少类型的判断,比如弱语言接收的任意类型,需要对类型判断逻辑更多,而强类型语言就可以减少不必要的类型判断。
  • 重构更安全可靠。

前期准备

安装TS

一、在安装ts之前,首先得安装node.js环境。

二、在项目根目录执行npm init -y生成package.json项目配置文件。

三、安装ts

全局安装,每个项目都使用同一个版本,不建议全局安装,不利用项目维护

npm install -g typescript

本地安装,在当前项目安装指定版本的ts,一般都是在开发环境才使用,因为运行时ts会编译成js。

npm install typescript -D

安装成功会在项目根目录下生成一个node_modules目录,在bin下有ts的命令符tsc,可以用命令tsc -v查看

在这里插入图片描述

在IDE安装ts-node可以直接运行ts代码

另外也可以在package.json文件可以看到ts的版本号

{"devDependencies": {"typescript": "^4.9.3"},// .....
}

TS配置文件

在根目录执行tsc --init命令生成tsconfig.json配置文件;如果根目录执行tsc --init命令无效,可以重启项目再次尝试;或者进入..\node_modules\.bin,再次执行命令 tsc --init,这样tsconfig.json就会生成在..\node_modules\.bin目录下,我们需要把文件移到项目根目录下。

tsconfig.json部分配置:

https://www.tslang.cn/docs/handbook/tsconfig-json.html

{"compilerOptions": {/*ts编译后所采用的es标准 */"target": "es2016",  /*输出的代码采用模块化方式*/"module": "commonjs",                            /*指定源代码的根文件夹*/"rootDir": "src",  "sourceMap": true,    /*编译后代码输出的位置(文件夹)*/"outDir": "dist",   }
}

如果一个目录下存在一个tsconfig.json文件,那么它意味着这个目录是TypeScript项目的根目录

基础类型

原始类型

  • 字符串string
  • 数字number:数字(浮点型、整型)、NaN
  • 布尔boolean
  • void:没有任何类型,比如函数的没有返回值类型时
  • undefined:本身作用不大
  • null:本身作用不大
// 基础类型:原始类型
// 声明变量或形参的类型,具体类型是定义为后置,与kotlin是类似的
const a: string = "foo" // 字符串
const b: number = 100 // 数字 (包括了浮点数)
const isDone: boolean = true // 布尔值const c: void = undefined //  void 默认值是 undefined
const d: null = null
const e: undefined = undefined// Symbol是es2015(es6+)标准库中的类型,如果"target": "es5" 就会报错
// 如果要使用es6的特性,将target配置成es2015
// 或者在不修改target的情况,在lib中引入标准库es2015
// 标准库就是内置对象所对应的声明,比如Promise、Symbol是ES6标准库中的对象
// 如果修改了tsconfig.json配置,运行还是报错,说明没有读取到我们修改后的配置文件,还是项目内置的配置即最原始配置es5
const s: symbol = Symbol("test")console.log(s)

其中undefined** 、**null可以是任何类型的子类型,也就是可以给类型变量赋值。当然是有提前的,如果启动了严格模式或者空检查,在编译器中会提示报错,如下:

在这里插入图片描述

在TS配置文件中关闭严格模式strict和空检测strictNullChecks

在这里插入图片描述

正常开发时是不会同时关闭strict、strictNullChecks,官方建议是开启strictNullChecks,如果一个函数的形参接收number或者string、null、undefined类型,在严格模式下是不允许number是不允许为null的,此时我们可以使用联合类型:number | string | null | undefined

// 联合类型
function foo(a: number | string | null | undefined): void {// 字符串模板`${..}`console.log(`foo: ${a}`)
}
foo(100)
foo("11")
foo(null)
foo(undefined)

object类型

// 基础类型:Object类型
// 是指非原始类型的其他类型都是object类型,比如:数组、函数、对象等等// export {} 消除ts文件变量重名的语法报错
export {}let a: object = function () {
}
let b: object = []
// 约束字面量对象,= 两边声明和赋值结构必须保持一致
let o: { name: string, age: number } = {name: "li", age: 12}// 报语法错误
// let x:object = 100

数组类型

定义数组类型有两种方式,第一种:在中括号[]前声明类型,第二种:数组泛型Array<number>

// 数组类型
// 有两种方式定义声明,第一种:在中括号[]前声明类型,第二种:数组泛型Array<number>// 第一种:声明具体的类型数组
let list: number[] = [1, 2, 4]
list.push(22)
// list.push("33") // 报错,只能是数字类型// let list = [1, 2, ""] // 联合类型数组,可以存放不同类型元素// 第二种let list2: Array<number> = [1, 2, 3]

如果数组不声明具体类型,就可以存放任何类型的元素,就是一个联合类型数组。

元组类型

元组类型Tuple:表示一个已知元素数量和类型的数组,各元素的类型不必相同

// 元组:表示一个已知元素数量和类型的数组,各元素的类型不必相同// [number, string] : 表示已知元素数量和类型的数组,初始化值必须对应数量和类型
// 有点类似联合类型
let tuple: [number, string] = [12, "lili"]// 访问元组,既然是数组,当然是可以通过下标访问
console.log(tuple[0], tuple[1])
//  通过解构也可访问元组
let [age, name] = tuple
console.log(age, name)// 元组的类型也可以是空类型,可以不初始化值
let tuple2: [number, string?] = [12]

枚举

在没有枚举的情况,JS定义常量,通常会用const修饰字面量对象,将常量定义成对象属性;这样有很强的语义化。

const orderState = {Delivered: 1,Received: 2,Returned: 3
}console.log(orderState.Delivered)

在TS提供了枚举类型,可以很好替换掉字面量常量对象的方式,具有更强的可读性。

// 数字枚举,默认是从0开始,逐个+1递增,如果第一个设置了初始值,会以这个初始值为起点,逐个+1递增
enum orderState {Delivered = 1, Received, Returned
}console.log(orderState)// 字符串枚举,要手动赋值enum order {Delivered = "已发货", Received = "已收货", Returned = "退货中"
}console.log(order)

函数类型

先看看JavaScript函数的弊端:

// js
function add(x, y) {return x + y
}// js函数的形参可以传任何类型的实参,返回值也是不确定的,根据形参来变化
// 类型的不确定性,会在运行时埋下安全隐患
add(1, true)

add()函数可以传入任何的实参类型,在编译期是不会提示报错的,如果我们期望是求和运算,传入实参不是数值,在运行时是达不到期望值的,因此JavaScript的类型不确定性,会给我们的代码在运行时埋下很大的安全隐患。

下面用TypeScript函数来改造:

// ts:声明形参类型和返回值类型确定了函数类型
// 返回值类型是后置的,这点与kotlin是相似的
// 参数类型:(x:number,y:number) => number
// 箭头左边是参数类型,右边是返回值类型,如果返回值没有类型,则用void表示
function add(x: number, y: number): number {return x + y
}// 参数类型 (x: number, y: number) => number
let myAdd = function (x: number, y: number): number {return x + y
}
myAdd(1,200.0)
// myAdd(1,"ddd") // 会推断实参类型,这里报错// 没有返回值的函数类型:(s:string) => void
function print(s: string): void {console.log(s)
}

在ts函数中,函数是有类型的,包括了参数类型返回值类型两个部分,当没有返回值时,则用void表示。

函数类型的表达:

在这里插入图片描述

如上面的myAdd()函数类型是:(x:number,y:number) => number,箭头左边是参数类型,右边是返回值类型。

在这里插入图片描述

会发现将冒号改成箭头,就是一个函数类型。

可选参数和默认参数

除了实参类型要求是一样的,在TypeScript里的每个函数参数都是必须的,传递给一个函数的参数个数必须与函数期望的参数个数一致。

在JavaScript中函数传入的参数都是可选的,没传参时默认值是undefined

在TypeScript函数实现可选参数,有两种方法:

  • 可选参数?修饰
  • 定义默认参数
//------------------- 可选参数和默认参数 ---------------// TypeScript里的每个函数参数都是必须的,传递给一个函数的参数个数必须与函数期望的参数个数一致。// ts也是箭头函数
// let buildName = (firstName: string, lastName: string) => firstName +" "+ lastNamefunction buildName(firstName: string, lastName: string) {return firstName + " " + lastName
}// 形参的实参类型必须传入两个,且类型是要与其一样的。
console.log(buildName("lisi", "bob"))// 在JavaScript中函数传入的参数都是可选的,没传参时默认值是undefined
// 在TypeScript要实现参数是可选的,可以在形参名称后面用?修饰,可选参数必须放在参数后面function buildName2(firstName: string, lastName?: string) {return firstName + " " + lastName
}let result2 = buildName2("bob")
let result22 = buildName2("bob", "sr")// 除了在形参名后面?修饰声明是可选参数,还可以用默认参数实现参数是可选的
// 默认参数不要求放在参数后面
function buildName3(firstName: string, lastName: string = "sr") {return firstName + " " + lastName
}let result33 = buildName3("bob")
let result333 = buildName3("bob", "sr")function buildName4(firstName: string = "lili", lastName: string = "sr") {return firstName + " " + lastName
}buildName4()
buildName4("lili", "bob")
buildName4(undefined, "bob")

可选参数和默认参数的区别是,可选参数?修饰必须放在参数的后面,而默认参数是没有这个要求的。

默认参数和可选参数的函数本质还是同一个函数类型,比如buildName2()buildName3()函数的类型都是(firstName: string, lastName?:string) => string

在这里插入图片描述

剩余参数

如果要函数需要接收多个不确定数量的同类型参数,默认参数和可选参数都是不适合的,它们只能表示某一个参数,在JavaScript里,可以通过arguments来访问所有传入的参数。在TypeScript里没有这个arguments,但可以通过剩余参数来达到目的。

剩余参数和JavaScript是相似的,在参数名称前面添加省略号...,用类型数组接收。

function buildName5(firstName: string, ...restName: string[]): string {return firstName + " " + restName.join(" ")
}buildName5("bob", "haha", "niu")

any任意类型

// JavaScript参数是可以接收任何类型的数据,由于TypeScript是有类型的,
// 如果要兼容JavaScript代码,必须要有一个类型来表示任意类型,这个类型就是any
// 和kotlin是一样的,any是所有类型的父类型。function stringify(obj: any) {return JSON.stringify(obj)
}console.log(stringify([1, 2, "3"]))
console.log(stringify({a: "11", b: "22"}))

类型断言

// 由于有任意类型any,当参数用any接收时,如果我们需要是某个具体类型时,这个就需要将any断言成具体类型
// 类型断言as并不是将类型强转,这与kotlin是不同的// 比如求和函数
function sum(params:any){if (typeof params == "string"){console.log("字符串 " +params)}if (typeof params == "number"){console.log("数值 "+params)}// 类型断言let a = params as numberconsole.log(a + 100)}sum("abc")
sum(22)

高级类型

交叉类型

交叉类型是指两个对象的属性并集合成一个新类型,前提是其中一个对象的属性或方法在另外一个对象都具有,也就是该类型具有所有类型的特性。

class Student {name: stringage: number
}class Teacher {name: stringage: number_occupation: stringconstructor(occ: string) {this._occupation = occ}log() {console.log("occupation: " + this._occupation)}}// 定义交叉类型,使用 &
let result: Teacher & Student;
// result = new Student() // 报错 不能赋值Student类型,Student没有age、_occupation log
result = new Teacher("老师")
result.log()
console.log(result)// 再次赋值
result = {name: "lili",age: 25,_occupation: "美术老师",log: function () {},// 不能有其他方法或属性,报错// test:function (){//// }
}console.log(result)let getUserInfo= function (info : Student & Teacher){console.log(info.name)console.log(info.age)console.log(info._occupation)info.log()
}getUserInfo(result)// getUserInfo(Student()) // 报错

日志:

老师
Teacher { _occupation: '老师' }
{ name: 'lili', age: 25, _occupation: '美术老师', log: [Function: log] }lili
25
美术老师

Teacher字面量对象类型具有所有的属性和方法,而Student是不满足该条件,因此是不能对交叉类型result赋值。

联合类型

联合类型是指两个类型的交集,取值可以为多种类型中的一种。
比如定义变量:

// 可能是string或者number类型,其中一种即可
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

如果联合类型是对象,那么只能访问两者公共的属性或方法:

//联合类型如果是对象,只能访问两者公共的属性或方法class Student {name: string ="学生"age: number = 16}class Teacher {name: string = "王老师"age: number = 25private readonly occupation: stringconstructor(occ: string) {this.occupation = occ}log() {console.log("occupation: " + this.occupation)}}let st : Student | Teacher
st = new Student()
console.log(st)
// 虽然是st的实例是指向Teacher对象,但只能方法联合类型的共同属性age和name,不能访问log()方法
st = new Teacher("教美术")
console.log(st)
// st.log() // 报错function getUserInfo(): Student | Teacher {return new Student()
}console.log(getUserInfo()) //只能访问 name 与 age属性function getUserInfo2(): Student | Teacher {return new Teacher("教数学")
}console.log(getUserInfo2())// 访问不到log()方法

日志:

Student { name: '学生', age: 16 }
Teacher { name: '王老师', age: 25, occupation: '教美术' }
Student { name: '学生', age: 16 }
Teacher { name: '王老师', age: 25, occupation: '教数学' }

与交叉类型不同的是,联合类型定义可以使用原始类型,而交叉类型不能用原始类型声明。

接口

接口是对行为的抽象,具体实现是由类来实现。

一个函数如果有多个形参,除了用剩余参数来接收,通常还会用对象来接收,除此还可以用接口的方式来接收实参,可以约束参数的类型。

// 用对象接收多个参数
function test(params: {name: string,age: number,sex: number,occ?: string,
} = {name: "", age: 0, sex: 0}) {console.log(params)
}test({name: "lili", age: 20, sex: 1})// 用接口接收多个参数
interface People {name: string;age: number;readonly sex: number; // 只读属性action?: string;// 可选属性
}function printUser(params: People) {console.log(params)
}// 对象类型要显式声明
let people: People = {name: "lisi", age: 18, sex: 1}
// people.sex = 0 // 只读属性,重新赋值会报错
let people2 = {name: "lili"}
printUser(people)
// printUser(people2) // ide会报错,缺少属性

当接口显式声明类型时,可以用字面量对象给变量赋值

let people: People = {name: "lisi", age: 18, sex: 1}

只读属性关键字readonly 声明,可选属性在变量名称加?

字面量对象或class类的属性都是可以这样

readonly sex: number; // 只读属性,只能赋值一次,通常在创建对象时赋值
action?: string;// 可选属性

在Java等面向对象中,对象的属性在编译期就已经确定了,ts作为前端语言,如果不能实现动态化添加属性,就失去js的灵活性了,因此ts提供了动态属性,动态属性又称任意属性。

动态属性用中括号声明[占位符]:

占位符就是声明属性名称是用字符串表示,基本是固定的。

interface Animal {name: string;// 动态属性[propName: string]: string;}

使用 [propName: string] 定义了动态属性取 string 类型的值:

let dog: Animal = {name: "狗",}
// 设置动态属性,其中 action 和 sex 就是属性名
dog.action = "跑"
// dog.sex = 1 // 与动态属性的值类型与声明类型不匹配会报错
dog.sex = "1"
console.log(dog)// 访问动态属性
console.log(dog.sex) // 对象.属性名
console.log(dog['sex']) // 对象[属性名] ,类似索引的方式

访问动态属性,用对象实例.属性名对象[属性名]

日志:

{ name: '狗', action: '跑', sex: '1' }
1
1

在接口Animal定义时,是没有action sex属性名,通常动态属性的设置可以灵活的添加属性。

在一个接口中只能定义一个动态属性,如果属性有多个类型的话,可以
通过联合类型来定义。比如sex动态属性的值可能是numberstring类型,使用联合类型定义:

interface Animal2 {name: string;[propName: string]: string | number;}let dog2: Animal2 = {name : "狗"
}dog2.sex = 1
dog2.sex = "母"

类的概念在面向对象语言中是不可或缺的,在es6就已经引进了类的概念。

用class声明一个类,constructor定义构造函数,通过new创建类的实例。

class People {name: string;// 构造函数constructor(name: string) {this.name = name}// 方法不用function定义,在es6也是可以省略的getName() {return this.name}}// 创建实例
let p = new People("lili")

在es6中类的实例属性的作用域是没有约束的,默认是公开的,如果定义私有属性,通常是使用’_'下划线来区分,外部实例还是可以访问的;在ts提供了和java类似的关键字来声明属性的作用域。

  • 默认是public
  • private 私有属性:只有在当前类下才可以访问,即使是子类也是不能访问的
  • protected 受保护属性:只有在类或子类内部才可以访问,类外部是无法访问的
class Animal {// 默认是publicname: string;// 在es6 如果定义私有属性,通常是使用'_'下划线来区分,外部实例还是可以访问的// 私有属性,只有当前类才可以访问,即使是子类也是不能访问的// 方法也是一样的private distanceInMeters: number = 0// 属性或参数用readonly修饰:表示只读,不能赋值// private readonly distanceInMeters: number = 0// 受保护属性protected _sex: number// 静态属性 : 与实例是无关的,不用实例就可以调用// 每个类都可以访问,但内存不是共享的static mouth = {size: 5}constructor(name: string, sex: number) {this.name = namethis._sex = sex}// 方法设置默认参数move(distanceInMeters: number = 0) {this.distanceInMeters = distanceInMetersconsole.log(`${this.name} moved ${distanceInMeters} m`)}// 在es6中提供了类的setter和getter方法,在java是常见的get sex(): number {return this._sex}set sex(sex: number) {this._sex = sex}// 静态方法static getMouth(): string {return Animal.mouth + ""}}

静态属性和方法与类的实例是无关的,直接通过类名来调用,与java是一样的,在内存中是共享的。

继承用extends关键字实现:

class Dog extends Animal {// 如果子类不定义构造函数,会默认使用父类的构造函数bark() {console.log('woof! woof!')}// 受保护属性: 只有在类或子类内部才可以访问,类外部是无法访问的getSex() {return  this._sex}
}let dog = new Dog("dog", 1)
dog.move(10)
dog.bark()
// dog._sex // 在类外部访问不了protected的属性
console.log("dog sex: " + dog.getSex())
Animal.mouth = {size: 10}
console.log(Animal.mouth)// dog.distanceInMeters // 报错,私有属性无法访问

多态是java的三大特性之一,在ts同样也是支持的,多态的三个必要条件:

  • 继承
  • 方法的重写
  • 父类的引用指向子类
class Snake extends Animal {constructor(name: string, sex: number) {super(name, sex);}// 重写父类的方法move(distanceInMeters: number = 5) {super.move(distanceInMeters);}
}class Horse extends Animal {constructor(name: string, sex: number) {super(name, sex);}move(distanceInMeters: number = 40) {super.move(distanceInMeters);}
}let snake = new Snake("Snake", 1)
// 虽然变量的类型声明是父类Animal,但move输出的是子类实例的信息
// 从而也反映了ts的类也是具有多态性
// 多态的三个必要条件:1、继承;2、重写;3、父类引用指向子类实例对象
// 当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
let horse: Animal = new Horse("Horse", 0)
snake.move() // Snake moved 5 m
horse.move() // Horse moved 40 m
console.log("snake sex: " + snake.sex) // 访问getter方法// 静态属性,与类的实例无关
Snake.mouth = {size: 20}
console.log(Snake.mouth)

抽象类不能直接创建实例,必须通过子类来创建实例

// 抽象类不能直接创建实例
abstract class Animal2 {// 抽象方法,子类必须实现该方法abstract makeSound(): void;move(): void {console.log('roaming the earch...');}
}class Duck extends Animal2 {// 子类实现抽象方法makeSound(): void {}}// 创建实例
let duck = new Duck()

接口是行为抽象化,没有具体实现,需要通过类来实现细节。

interface ClockInterface {currentTime: DatesetTime(d: Date)
}class Clock implements ClockInterface {currentTime: Date;setTime(d: Date) {this.currentTime = d}}

泛型

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。在面向对象语言中是十分重要的,优秀的代码都离不开对泛型的理解与使用,能将代码模板化,提高复用性。

比如下面这个例子,在没有使用泛型的情况下:

// id形参为了可以接收任意类型,声明any类型,但返回类型无法确定。
function identity(id: any) {return id
}identity("23")identity(23)

使用泛型后,在使用期间就知道了具体类型。

// 用泛型就可以解决上面的问题
function identity2<T>(id: T): T {return id
}
// 指明泛型T的具体类型是number 
let id = identity2<number>(12)
console.log(id)

泛型也是有类型的,比如identity2

// <T>(id: T) => T 就是identity2函数的类型
let myId: <T>(id: T) => T = identity2
myId<string>("AABB")

泛型在类中的应用:

class Generic<T> {value: T;// 泛型不能做运算// add(x:T,y:T):T{//     return x + y// }// 定义一个泛型函数类型的属性,类型是(x: T, y: T) => Tadd: (x: T, y: T) => T
}let generic = new Generic<number>()
// 没有add变量赋值,运行会报错
// generic.add(1,2) // 报错: generic.add is not a function// 赋值后,再调用就不会报错
generic.add = function (x, y) {return x + y
}
let ab = generic.add(1, 3)
console.log(ab)//-- 泛型约束
abstract class Animal {abstract getName(): string
}class Dog extends Animal {getName(): string {return "Dog";}}class Bee extends Animal {getName(): string {return "Bee";}}class Car {getName(): string {return "Car";}
}function createAnimal<T extends Animal>(a: new () => T): T {// 在java中,泛型是不可以直接在方法中创建实例,因此在编译时会被檫出// 在kotlin中 可以通过内联特化和反射创建泛型的实例return new a()
}let d = createAnimal<Dog>(Dog)
console.log(d.getName())
let b = createAnimal(Bee)
console.log(b.getName())let c = createAnimal(Car) // ???
console.log(c.getName())

类型别名

// 类型别名:给一个类型取一个新名字
type Name = string
type NameResolver = () => string
type  NameOrResolver = Name | NameResolver// 函数返回值是一个字符串,形参NameOrResolver是一个联合类型,是一个函数类型或字符串
function getName(n: NameOrResolver): Name {if (typeof n === 'string') {return n;} else {return n();}
}

参考

  • https://segmentfault.com/a/1190000040220033
  • https://www.tslang.cn/docs/home.html
  • https://ts.xcatliu.com/introduction/what-is-typescript.html
  • https://juejin.cn/post/7106079206160891918

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/839.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

飞书ChatGPT机器人 – 打造智能问答助手实现无障碍交流

文章目录 前言环境列表1.飞书设置2.克隆feishu-chatgpt项目3.配置config.yaml文件4.运行feishu-chatgpt项目5.安装cpolar内网穿透6.固定公网地址7.机器人权限配置8.创建版本9.创建测试企业10. 机器人测试 前言 在飞书中创建chatGPT机器人并且对话&#xff0c;在下面操作步骤中…

Flask结合gunicorn和nginx反向代理的生产环境部署及踩坑记录

个人博客&#xff1a;https://xzajyjs.cn 前言 之前自己写的flask使用gunicorn上线生产环境没有什么问题&#xff0c;但是最近搭建了一个现成的flask项目&#xff0c;当使用python直接运行时不会有问题&#xff0c;而使用gunicorn时则会出现一些问题。 部署过程 运行测试 这…

听GPT 讲K8s源代码--pkg(一)

在 Kubernetes 代码仓库中&#xff0c;pkg/api和pkg/apis目录都包含用于定义 Kubernetes API 对象的代码&#xff0c;但它们的作用略有不同。 pkg/api目录包含 Kubernetes 的旧版本 API 对象定义&#xff0c;这些定义在 Kubernetes 1.7 版本之前使用。这些对象定义已经过时&…

k8s 持久化存储

我们继续来查看 k8s 的卷&#xff0c;上一次我们分享了将磁盘挂载到容器中&#xff0c;empyDir 和 gitRepo 都是会随着 pod 的启动而创建&#xff0c;随着 pod 的删除而销毁 那么我们或许会有这样的需求&#xff0c;期望在 pod 上面读取节点的文件或者使用节点的文件系统来访问…

Spring Boot 中的 Redis 的数据操作配置和使用

Spring Boot 中的 Redis 的数据操作配置和使用 Redis 是一种高性能的 NoSQL 数据库&#xff0c;它支持多种数据结构&#xff0c;包括字符串、哈希、列表、集合和有序集合。Redis 还提供了丰富的命令&#xff0c;可以对数据进行快速的 CRUD 操作。Spring Boot 是一个基于 Sprin…

基于单片机智能加湿器 水位防干烧加湿器的设计与实现

功能介绍 以51/STM32单片机作为主控系统&#xff1b;LCD1602液晶显示当前温湿度&#xff0c;当前模式&#xff0c;湿度下限;按键设置湿度下限&#xff0c;当湿度低于下限时开启加湿器;水位传感器检查加湿器是否有水&#xff0c;如果没有水到话加湿器不进行工作&#xff0c;蜂鸣…

怎么用PDF24 Tools工具在线进行PDF文件合并

PDF文件是经常会被用到&#xff0c;它在我们的日常生活和工作中扮演着重要的角色。PDF文件合并是将多个PDF文件合并为单个文件&#xff0c;这个过程通常是为了方便管理多个PDF文件&#xff0c;或者将多个PDF文件合并为一个整体以便于共享或打印。既然如此&#xff0c;如何快速合…

kotlin forEach循环return/break

kotlin forEach循环return/break fun main(args: Array<String>) {var a mutableListOf("0", "1", "2", "3", "4")var b mutableListOf<String>()a.forEachIndexed { index, s ->if (index > 2) {retu…

【Mac使用笔记】之 Homebrew

Homebrew更新&#xff1a; brew update && brew upgrade 当出现错误&#xff1a; fatal: couldnt find remote ref refs/heads/master 执行&#xff1a; brew tap --repair Ruby安装&#xff1a; 1、查看当前Homebrew版本&#xff1a; brew --version2、查看当前…

libbpf-bootstrap 开发指南:概念与如何安装

目录 概念 如何安装& 使用 git 地址 使用git clone 下载代码 安装依赖环境 安装libbpf 编译example 概念 libbpf-bootstrap 是一个项目&#xff0c;旨在帮助开发者快速启动和开发使用 eBPF (Extended Berkeley Packet Filter) 和 libbpf 的程序。eBPF 是一种可以在…

TTX1994-可调谐激光器控制系统

花了两周时间&#xff0c;利用下班时间&#xff0c;设计了一个ITLA可调谐激光器控制系统&#xff0c;从硬件到软件。下面这个图片整套硬件系统&#xff0c;软件硬件都自己设计&#xff0c;可以定制&#xff0c;做到单片机问题也不大。相当于一套光源了 这是软件使用的界面&…

Kafka 概述、Filebeat+Kafka+ELK

Kafka 概述、FilebeatKafkaELK 一、为什么需要消息队列&#xff08;MQ&#xff09;1、使用消息队列的好处2、消息队列的两种模式 二、Kafka 定义1、Kafka 简介2、Kafka 的特性3、Kafka 系统架构 三、部署 kafka 集群1.下载安装包2.安装 Kafka3.Kafka 命令行操作 四、Kafka 架构…

基于linux串口实现语音刷抖音

目录 1.开发逻辑图及模块 2.编程实现语音和开发板通信 3.手机接入Linux热拔插相关,打开手机开发者模式允许USB调试 4.用shell指令来操作手机屏幕&#xff0c;模拟手动滑屏幕 5.最终主程序代码 1.开发逻辑图及模块 逻辑图&#xff1a; 模块 &#xff08;1&#xff09;语音…

运维小知识(二)——Linux大容量磁盘分区及挂载

centos系统安装&#xff1a;链接 目录 1.&#x1f353;&#x1f353;命令格式化磁盘 2.&#x1f353;&#x1f353;大容量硬盘分区 3.&#x1f353;&#x1f353;自动挂载 整理不易&#xff0c;欢迎一键三连&#xff01;&#xff01;&#xff01; 新系统装完之后&#xff0…

C语言图书管理系统

一&#xff0c;开发环境 操作系统&#xff1a;windows10, windows11, linux, mac等。开发工具&#xff1a;Qt, vscode, visual studio等开发语言&#xff1a;c 二&#xff0c;功能需求 1. 图书信息管理&#xff1a; 这个功能的主要任务是保存和管理图书的所有信息。这应该包…

数据库多表查询作业

数据库多表查询作业 创建数据库 插入数据 mysql> insert into student values(901,张老大,男,1985,计算机系,北京市海淀区),-> (902,张老二,男,1986,中文系,北京市昌平市),-> (903,张三,女,1990,中文系,湖南省永州市), -…

opencv实战--角度测量和二维码条形码识别

文章目录 前言一、鼠标点击的角度测量二、二维码条形码识别 前言 一、鼠标点击的角度测量 首先导入一个带有角度的照片 然后下面的代码注册了一个鼠标按下的回调函数&#xff0c; 还有一个点的数列&#xff0c;鼠标事件为按下的时候就记录点&#xff0c;并画出点&#xff0c;…

【软件测试】Git 远程仓库的使用(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 查看远程仓库 想…

SQL性能规范

一、随聊 记录一下吧&#xff0c;2023年7月13日00:11:11&#xff0c;现在的状态真的很&#xff0c;忙&#xff0c;干不完的活&#xff0c;希望巨大的压力&#xff0c;能够让自己快速成长&#xff0c;回想我这一路&#xff0c;21年大专毕业&#xff0c;用一年时间熟悉软件&…

STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066

STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066 Proteus仿真小实验&#xff1a; STM32 Proteus仿真医用仓库环境控制系统紫外线消毒RS232上传CO2 -0066 功能&#xff1a; 硬件组成&#xff1a;STM32F103R6单片机 LCD1602显示器DHT11温度湿度电位器模拟…