一、概述
TypeScript是一门基于JavaScript之上的语言,重点解决JS自有系统的不足,通过TypeScript可以大大提高代码的可靠程度
二、知识点
1、强弱类型
类型安全角度分类,分为强类型和弱类型。
强类型不允许任意的隐式类型转化,在语言层面限制函数的实参类型必须与形参类型相同(Java类中的函数),弱类型则允许隐式类型转化,强类型有更强的类型约束,弱类型几乎没有什么约束
例如:Java中的形参与实参在类型和个数上一定要保持一致,但是JavaScript则没有这些限制
2、静态动态类型
类型检查角度分类分为静态类型和动态类型
静态类型:一个变量声明以后他的类型就是明确的,声明过后,它的类型不允许修改
动态类型:运行阶段才能够明确变量类型,而且变量类型也可以随时变化,变量是没有类型的,但是变量存放的值是有类型的。
语言类型的维度
3、JS类型系统特征
弱类型并且是动态类型,没有编译环节,
4、弱类型的问题
(1)没有编译过程,所以错误只会在执行的过程中被抛出,例如下边的代码,会在运行一秒之后才会报错。
const obj = {}
setTimeout(()=>{
obj.fun() // obj里面没有fun()函数 一秒后抛出错误
},1000)
(2)由于类型不固定造成的功能可能发生改变
function add(a,b){
return a+b
}
console.log(add(1,2)) //3
console.log(add(1,"2")) // 12
5、强类型的优点
(1)错误更早暴露
(2)代码更智能,编码更准确
(3)重构更牢靠
(4)减少不必要的类型判断
6、Flow
JavaScript的类型检查器,能够弥补javascript弱类型的弊端,vue、react项目里都可以看到Flow的使用
它通过类型注解的方式来检查代码里的异常,Flow只是一个小工具
(1) 安装使用
npm install flow-bin
//安裝
npm run flow init
//初始化配置文件
npm run flow status
npm run flow check
npm run flow stop
(2)使用示例,注释添加 @flow,方法中添加参数类型
// @flow
//参数类型为number,返回值类型也为number
function addSum:number(a:number,b:number){
return a+b
}
console.log(addSum(1,'2'))
let num:number = 1 //变量
(3)yarn安装
npm install -g yarn // 全局安装
yarn --version //查看系统版本
/** yarn : 无法加载文件 C:\Users\张艳杰\AppData\Roaming\npm\yarn.ps1,因为在此系统上禁止运行脚本。有关详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Exec
ution_Policies。
**/
报错处理:
1、搜索powershell ,右键以管理员身份运行
2、将计算机上的 执行策略更改为 RemoteSigned,执行:set-ExecutionPolicy RemoteSigned
3、查看执行策略:get-ExecutionPolicy,即可解決
继续查看版本
yarn --version //1.22.4
(4)移除注释
添加过注释的文件上hi没有办法运行的,所以要将注释移除掉,添加flow-remove-types模块
yarn add flow-remove-types --dev
yarn flow-remove-types . -d dist // .表示当前目录,dist表示去除注释以后的js文件的输出路径
(5)只有通过命令才能检测到数据,比较麻烦,所以为了避免这个问题,我们可以vscode编辑器安装插件”Flow Language Support“,在代码保存过后就直接进行检测
(6)类型推断
在我们不标注参数类型的情况下,直接报错,说明在编译的过程中进行了校验,这种行为叫做类型推断
function power(a,b){
return a**b
}
power(1,'3')
(7)类型注解
函数中参数可加
function addSum(a:number,b:number){
return a + b
}
变量可加
let num : number = 100
num = '12' //红色波浪线提示
函数返回值可加
function addSum :number(a:number,b:number){
return a + b+"" //红色波浪线提示
}
(8)原始类型
const a: String = 'str'
const b: Number = 100
const b:Number = NaN // NaN是number类型
const c: Number = Infinity // 无穷大 number类型
const d: null = null
const e: void = undefined // Flow中undefined类型用void表示
const f: symbol = Symbol()
(9)数组类型
两种方式,一种是Array<>,一种是数组定义,也可以在一个数组中有多个值的类型定义
const arr: Array<number> = [1,2,3,4,'12']
const arr1: number[] = [1,2,'3']
const arr2:[string,number] = ['foo',12]
(10)对象类型
声明为一个对象使用{ }
const obj: {}= {name:'123'}
声明对象中必须要有某一个属性
const obj1: {name:String,age:number} = {name:'123'} //沒有age属性,红色波浪
声明一个对象可有可无,屬性后添加?即可
const obj1: {name:String,age?:number} = {name:'123'}
对属性类型进行校验
const obj2: {[string]:number} = {}
(11)函数类型
function fun(callback:(string,number)=>number){
callback('string',100)
}
fun(function(str,n){ //必須传递参数为strign和number的值
return n
})
(12)特殊类型
字面量类型(“|”表示或者)
let str: 'success' |'fail' = 'fail12' //红色波浪 值只能为success和fail中的一个
可以别名使用
type types = string | number
let num: types = '23'
const gender: ?number = undefined
const gender: number | null | void = undefined
(13)Mixed和Any
Mixed,所有类型的集合,但是使用的时候也要进行类型的判断才可以,所以说是强类型
function testMixed(value: mixed){
if(typeof value === 'number'){
value*value
}
if(typeof value === 'string'){
value.substr(1)
}
}
Any,与mixed一样,但是使用的时候不用进行类型的判断
function testAny(value: any){
value*value
value.substr(1)
}
Flow还有其他类型 https://flow.org/en/docs/types/
三、TypeScript
typeScript是一门基于JavaScript之上的一门语言。是JS的扩展集和超集。任何一种javascript运行环境都支持。相比于上面介绍的Flow,功能更为强大,生态也更健全、更完善。vue.js3.0开始使用TypeScript取代Flow
yarn init --yes
yarn add typescript --dev //安装typeScript模块
yarn tsc .\01-prepare.ts
yarn tsc --init //tsconfig.json
(1)原始数据类型
const a: string = '123'
const b: number = 123 // NaN Infinity
const c: boolean = true //false
const d: void = undefined
const e: null = null
const f: undefined = undefined
(2)作用域
不同文件当中会有相同变量名称问题,使用立即执行函数
(function(){
let a = 1
})()
或者
let a = 123
export {} //确保跟其他示例成员没有冲突
(3) Object 类型
object类型指的是除了原始类型以外的所有类型,可以通过字面量的方式定义
const foo: object = function(){} // [] {}
const obj: { foo : number } = { foo : 123 }
(4)数组类型
const arr:number[] = {1,2,3}
const arr1:Array<Number> = {1,2,3}
function sum(...args:number[]) {
return args.reduce((prev,current) => prev+current,0)
}
sum(1,2,3)
(5)元组类型
元组:明确元组数量以及每个元组类型的数组,类型可以不相同
const tuple: [number,string] = [18,'123'] //类型不一致或者是个数不一致都会导致报错
(6)枚举类型
如果涉及到不同得数值代表某种状态,可以用的枚举,使用方式和对象是一样得
一种常见的数据结构,如果设置第一个值为x,如果不设置,x=0,则后续值分别加1,调用时候用 枚举名称.枚举的成员名称,
enum MyStatus{
one=1,
two,
three
}
console.log(MyStatus.three)
字符串也可以进行枚举,此时需要每个都要进行赋值。使用枚举类型,编译转换的时候不会被移除掉,
enum MyStatus{
one=‘aaa’,
two='bbb',
three='ccc'
}
console.log(MyStatus.three)
(7)函数类型
直接定义在参数后,返回值也可固定类型
function add(num1:number,num2:number) :number{
return num1+num2
}
add(100,200) //参数固定,类型固定
function add1(num1:number,num2:number = 10) :number{
return num1+num2
}
add1(1)
function func1(a:number, b: number = 10, ...reset:number[]):string {
return 'func1'
]
func1(1,2,3)
(8)任意类型
any不会进行类型检查,所以会出现安全问题,
function trans(str:any){
JSON.stringify(str)
}
(9)隐式类型推断
如果不声明变量的类型,则会根据赋值进行类型推断,并不可进行改变
let tes = 18
tes = '123' //报错,因为tes被隐式推断为了number,相当于已经定义了,let tes:number = 18
但是如果不声明变量的类型,并且不赋值,则类型为any,可以赋值任意类型的数据
let test2
test2 = 18
test2 = '123'
(10)类型断言
开发者可以根据代码情况 知道变量是什么类型的
export {}
const nums = [110, 120, 119, 112]
const res = nums.find(i => i> 0)
// 断言两种方式
const num1 = res as number // 推荐
const num2 = <number>res // jsx下不能使用
类型断言并不是 类型转换,类型转换时代码运行时的概念,类型断言只是编译过程的概念
(11) 接口
interface,约定对象的结构,可选成员要添加?,只读成员,属性不能被修改,添加readonly
interface Post {
title: string
content: string,
time?:string,
readonly name:string
}
export {}
interface Post{
title: string,
content: string
}
function printPost(post: Post) {
console.log(post.title)
console.log(post.content)
}
printPost({
title: 'aa',
content: 'bb'
})
创建动态成员
interface Cache {
[prop: string]: string
}
const cache: Cache = {}
cache.foo = 'value'
cache.bar = 'value2'
面试题:interface与type区别及各自使用场景
在TypeScript中,interface 和 type 都用于定义类型,但它们之间存在一些关键的区别和使用场景。具体分析如下:
- 语法差异:interface 使用关键字 interface 定义,例如 interface Person { name: string; };而 type 使用关键字 type 定义,并通常需要使用等号 =,例如 type Person = { name: string; }。
- 合并能力:interface 支持声明合并,即当有多个同名接口时,它们会自动合并为一个接口,合并后的接口会包含所有同名接口的成员。相反,type 不支持声明合并,如果有多个同名的类型别名,会导致编译错误。
- 可扩展性:interface 可以使用 extends 关键字来继承其他接口或类,这提供了一种方式来实现接口的复用和扩展。而 type 则通过交叉类型(使用 &)进行扩展,但不能使用 extends 关键字。
- 兼容性:在进行类型兼容性检查时,interface 会进行"兼容性递归检查",只要目标类型满足源类型的成员要求,就认为类型兼容。type 没有这样的兼容性检查流程。
- 使用场景:interface 通常用于描述对象的形状,定义必须实现的属性和方法,适用于那些需要明确定义结构且可能被类实现的场景。例如定义一个具有固定属性和方法的用户接口:
interface User {
name: string;
age: number;
sayHello(): void;
}
type 则适合创建复杂的类型别名,如联合类型、交叉类型、元组等。它更加灵活,可以用在任何类型的别名上。例如定义一个可以接收字符串或数字的 ID 类型:
type ID = string | number;
或者使用交叉类型组合多个接口的属性:
type AdminUser = User & {
isAdmin: boolean;
};
综上所述,interface 更适合定义对象的结构并被类实现,以及在需要合并多个声明时使用。而 type 则更加通用和灵活,能够表示任意类型的别名,特别适合于表达复杂的类型组合。在具体选择使用哪一个时,应根据具体的使用场景和需求来决定。
- interface 和 type 的定义
interface Person {
name: string;
age: number;
}
type Point = {
x: number;
y: number;
};
在上面的例子中,我们定义了一个 Person 接口和一个 Point 类型。Person 接口要求一个对象具有 name 和 age 属性,而 Point 类型则表示一个具有 x 和 y 属性的对象。
- interface 和 type 的语法差异
虽然 interface 和 type 有相似的作用,但它们的语法有一些细微的差异。
interface 的语法比较简单,我们只需要使用关键字 interface 加上一个名称来定义它
interface Person {
name: string;
age: number;
}
type 的语法稍微有些复杂一些,我们需要使用关键字 type 并给它一个名称:
type Point = {
x: number;
y: number;
};
需要注意的是,type 还可以使用 = 来定义一个类型别名:
type MyString = string;
上面的代码定义了一个类型别名 MyString,它表示一个字符串类型。
- interface 和 type 的扩展能力
在 interface 中,我们可以使用 extends 关键字来扩展其他接口。这意味着一个接口可以继承另一个接口的属性和方法。
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
}
在上面的例子中,我们定义了一个 Animal 接口和一个 Dog 接口。Dog 接口通过 extends 关键字扩展了 Animal 接口,因此它拥有了 Animal 接口中的 name 和 age 属性,并且还额外定义了一个 breed 属性。
与 interface 不同,type 使用 & 来扩展
其他类型。这意味着一个类型可以合并其他类型的属性。
type Person = {
name: string;
age: number;
};
type Employee = {
company: string;
};
type EmployeePerson = Person & Employee;
在上面的例子中,我们定义了一个 Person 类型和一个 Employee 类型。然后,我们使用 & 将这两个类型合并成了一个新的类型 EmployeePerson。这样,EmployeePerson 就拥有了 Person 和 Employee 中的所有属性。
- interface 和 type 的可选属性和只读属性
在 TypeScript 中,我们经常需要定义可选属性和只读属性。那么,interface 和 type 如何处理这些特性呢?让我们来看看.
在 interface 中,我们可以使用 ? 来定义可选属性,使用 readonly 来定义只读属性。
interface Person {
name: string;
age?: number; // 可选属性
readonly id: number; // 只读属性
}
在上面的例子中,age 属性是可选的,而 id 属性是只读的。只读属性表示一旦赋值后就不能再被修改。
在 type 中,我们使用 ? 和 readonly 关键字来定义可选属性和只读属性。
type Person = {
name: string;
age?: number; // 可选属性
readonly id: number; // 只读属性
};
- interface 和 type 的适用场景
interface 适用于描述对象的结构和行为。如果你需要定义一个对象的属性和方法,并且希望其他对象可以通过继承来共享这些属性和方法,那么 interface 是一个不错的选择。
interface Shape {
name: string;
area(): number;
}
上面的例子中,我们定义了一个 Shape 接口,它包含了一个 name 属性和一个计算面积的 area 方法。如果有多个形状,比如 Circle 和 Rectangle,都需要具有这些属性和方法,那么它们可以分别实现 Shape 接口。
type 则适用于定义自定义类型。当你需要创建一个具有特定结构的类型,而不仅仅是对象时,type 是一个更好的选择。
type MyString = string;
上面的例子中,我们创建了一个类型别名 MyString,它表示一个字符串类型。这样,在代码中可以直接使用 MyString 来表示字符串类型。
- interface 和 type 的相互转换
有时候,我们可能需要将 interface 转换为 type,或者将 type 转换为 interface。在 TypeScript 中,这是可以实现的。
interface 转换为 type
要将 interface 转换为 type,我们可以使用 type 关键字和 typeof 运算符。
interface Person {
name: string;
age: number;
}
type PersonType = typeof Person;
在上面的例子中,我们使用 typeof 运算符将 Person 接口转换为对应的类型 PersonType。这样,PersonType 将拥有 Person 接口中定义的所有属性和方法。
type 转换为 interface
要将 type 转换为 interface,我们可以使用 interface 关键字并重新定义类型的结构。
type Point = {
x: number;
y: number;
};
interface PointInterface extends Point {}
在上面的例子中,我们使用 interface 关键字将 Point 类型转换为对应的接口 PointInterface。这样,PointInterface 将具有 Point 类型中定义的属性。
结论
到这里,我们对于 TypeScript 中的 interface 和 type 已经有了深入的了解。虽然它们在某些方面有一些差异,但它们都是用来定义类型的强大工具。在实际开发中,你可以根据具体情况选择使用 interface 或 type,或者甚至将它们结合起来使用。
(12)类
使用class关键字
class Person {
name: string
age: number
constructor(name: string,age:number) {
this.name = name
this.age =age
}
sayHi(mes: string):void {
console.log(`I am ${this.name},${msg}`)
}
}
访问修饰符
public:默认修饰符 公有的
private:私有的 类的内部访问 外部不能访问,子类也不能访问
protected:受保护的 外部不能访问 允许子类访问
static 静态属性
export {}
class Persion {
public name: string
private age: number
protected gender: boolean
constructor(name: string,age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi(msg: string):void {
console.log(`I am ${this.name},${age}`)
}
}
class Student extends Persion {
constructor(name:string, age: number) {
super(name, age)
console.log(this.gender)
}
}
public共有的,默认属性,如果想在类的外面访问内部属性方法
class Persion {
sayHi():void {
console.log(`I am lcw`)
}
}
new Persion().sayHi()
static 静态属性,可以不通过 new构造函数取访问内部的属性方法
class Persion {
static sayHi():void {
console.log(`I am lcw`)
}
}
Persion.sayHi()
类的只读属性 readonly
export {}
class Persion {
public name: string
private age: number
protected readonly gender: boolean // 设置只读属性
constructor(name: string,age: number) {
this.name = name
this.age = age
this.gender = true
}
sayHi(msg: string):void {
console.log(`I am ${this.name},${mag}`)
}
}
class Student extends Persion {
constructor(name:string, age: number) {
super(name, age)
console.log(this.gender)
}
}
(13) TypeScript类与接口
export {} //确保跟其他示例没有成员冲突
interface Eat {
eat(food: string): void
}
interface Run {
run(distance: number): void
}
class Persion implements Eat,Run {
eat(food: string): void {
console.log(`优雅的进餐:${food}`)
}
run(distance: number) {
console.log(·直立行走:${distance}·)
}
}
class Animal implements Eat,Run {
eat(food: string): void {
console.log(`优雅的进餐:${food}`)
}
run(distance: number) {
console.log(·直立行走:${distance}·)
}
}
(14)抽象类
abstract
export {} //确保跟其他示例没有成员冲突
abstract class Animal {
eat(food: string):void {
console.log(`吃:${food}`)
}
abstract run(distance: number): void
}
// 上面定义了抽象方法 下 面就要实现
class Dog extends Animal {
run(distance: number): void {
console.log('四脚爬行',distance)
}
}
const d = new Dog()
d.eat('恩希玛')
d.run(100)
(15)泛型
传值的形式指定类型
// 泛型
export {} // 确保跟其它示例没有成员冲突
function createNumberArray (length: number, value: number): number[] {
const arr = Array<number>(length).fill(value)
return arr
}
function createStringArray (length: number, value: string): string[] {
const arr = Array<string>(length).fill(value)
return arr
}
function createArray<T> (length: number, value: T): T[] { // 类型不确定 用传参的方式传入 类型,
const arr = Array<T>(length).fill(value)
return arr
}
// const res = createNumberArray(3, 100)
// res => [100, 100, 100]
const res = createArray<string>(3, 'foo')
Ts中的泛型函数总结keyof
keyof
TypeScript中的keyof操作符,是将一个类型映射为它所有成员名称的联合类型。
interface Person {
name: string;
age: number;
gender: string;
}
type P = keyof Person; // "name" | "age" | "gender"
// 我们可以看到,keyof将Person这个对象类型映射成了一个联合类型
// 因此我们可以更方便的操作这个联合类型
Partial
Partial的作用是将传入的属性变成可选项,原理就是使用keyof拿到所有属性名,然后再使用in[遍历],T[P]拿到相应的值。
type Partial<T> = { [P in keyof T]?: T[P] }
//作用:生成一个新类型,该类型与 T 拥有相同的属性,但是所有属性皆为可选项
看看使用实列
interface Foo {
name: string
age: number
}
type Bar = Partial<Foo>
// 相当于
type Bar = {
name?: string
age?: number
}
Required
Required 的作用是将传入的属性变为必选项,原理是使用-?将可选项的?去掉。与之对应的还有个+?。
type Require<T> = { [p in keyof T]-?: T[P] }
例子
interface Foo {
name: string
age?: number
}
type Bar = Required<Foo>
// 相当于
type Bar = {
name: string
age: string
}
Readonly
Readonly的作用是将传入的属性变为只读选项
type Readonly<T> = { readonly [P in keyof T]: T[P]; };
Mutable
Mutable的作用是将传入属性的readonly移除。
This is useful as a lightweight builder for a type with readonly properties (especially useful when some of the properties are optional and conditional logic is needed before assignment).
type Mutable<T> = {
-readonly[P in keyof T]: T[P]
};
type Mutable<T> = {-readonly[P in keyof T]: T[P]};
interface Foobar {
readonly a: number;
readonly b: number;
readonly x?: string;
}
function newFoobar(baz: string): Foobar {
const foobar: Mutable<Foobar> = {a: 1, b: 2};
if (shouldHaveAnX(baz)) {
foobar.x = 'someValue';
}
return foobar;
}
Record
Record的作用是将K中所有属性的值转化成T类型
Record<K,T>构造具有给定类型T的一组属性K的类型。在将一个类型的属性映射到另一个类型的属性时,Record非常方便。他会将一个类型的所有属性值都映射到另一个类型上并创造一个新的类型.
type Record<K extends keyof any, T> = {
[P in K]: T;
};
type petsGroup = 'dog' | 'cat' | 'fish';
interface IPetInfo {
name:string,
age:number,
}
type IPets = Record<petsGroup, IPetInfo>;
const animalsInfo:IPets = {
dog:{
name:'dogName',
age:2
},
cat:{
name:'catName',
age:3
},
fish:{
name:'fishName',
age:5
}
}
Pick
Pick的作用是从T中取出一系列K的属性
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
interface Person {
name: string;
age: number;
id: number;
sex: 0 | 1;
}
// 问女生年纪不太礼貌,所以我们不需要 age 这个属性
type Woman = Pick<Person, "name" | "id">;
// 此时 Woman 等效于 Female
interface Female {
name: string;
id: number;
}
Exclude
Exclude的作用是从T中找出U中没有的元素
type Exclude<T, U> = T extends U ? never : T;
type A = Exclude<'key1' | 'key2', 'key2'>
// 'key1'
这个定义就利用了条件类型中的分配原则,来尝试将实例拆开看看发生了什么:
type A = `Exclude<'key1' | 'key2', 'key2'>`
// 等价于
type A = `Exclude<'key1', 'key2'>` | `Exclude<'key2', 'key2'>`
// =>
type A = ('key1' extends 'key2' ? never : 'key1') | ('key'2 extends 'key2' ? never : 'key2')
// =>
// never是所有类型的子类型
type A = 'key1' | never = 'key1'
Extract
高级类型Extract和上面的Exclude刚好相反,它是将第二个参数的联合项从第一个参数的联合项中提取出来,当然,第二个参数可以含有第一个参数没有的项。
下面是其定义和一个例子,有兴趣可以自己推导一下
type Extract<T, U> = T extends U ? T : never
type A = Extract<'key1' | 'key2', 'key1'> // 'key1'
Omit
Omit的作用是忽略对象的某些属性功能
它的作用主要是:以一个类型为基础支持剔除某些属性,然后返回一个新类型。
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Person = {
name: string;
age: string;
location: string;
};
type PersonWithoutLocation = Omit<Person, 'location'>;
// PersonWithoutLocation equal to QuantumPerson
type QuantumPerson = {
name: string;
age: string;
};
(16)类型声明
// 类型声明
import { camelCase } from 'lodash'
declare function camelCase (input: string): string
const res = camelCase('hello typed')
(17)polyfill
polyfill是实现兼容问题,
core-js 基本上把能polyfill API都实现了
(18)关于this的总结
1、沿着作用域向上找最近的一个function(不是箭头函数),看这个function最终是怎样执行的
2、this的指向取决于所属function 的调用方式,而不是定义;
3、function调用一般分为以下几种情况:
-
1、作为函数调用,即:foo()
指向全局对象(globalThis),注意严格模式的问题,严格模式下是undefined -
2、作为方法调用,即:foo.bar()
指向最终调用这个方法的对象
-
3、作为构造函数调用,即:new Foo()
指向一个新对象,即: Foo {} -
4、特殊调用,即:foo.call() / foo.apply() / foo.bind()
参数指定成员
4、找不到所属的function,就是全局对象
(19) InstanceType
InstanceType 是 TypeScript 中的一个内置类型操作符。它用来获取一个类构造函数的实例类型。
具体而言,给定一个类的构造函数类型 T,InstanceType 将返回该类的实例类型。
class MyClass {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
type MyInstance = InstanceType<typeof MyClass>;
let instance: MyInstance = new MyClass("John", 25);
console.log(instance); // 输出:MyClass { name: "John", age: 25 }
在上面的示例中,typeof MyClass 获取到 MyClass 的构造函数类型。然后,InstanceType 返回 MyClass 的实例类型,即 { name: string; age: number; }。
通过将 InstanceType 赋给变量 MyInstance,我们可以使用 MyInstance 类型定义变量 instance,并实例化 MyClass 类的对象。最后,打印 instance 将会输出该对象的属性值。
InstanceType 在 TypeScript 中非常有用,可以用于对类的实例进行类型推断或定义通用函数,以便能够接受任意类的实例作为参数。
(20)联合类型和交叉类型
- 基础类型联合
let a: string | number;
a = 1; //ok
a= "a"//ok
- 交叉类型 &
interface People {
age: number,
height: number
}
interface Man{
sex: string
}
const lilei = (man: People & Man) => {
console.log(man.age)
console.log(man.height)
console.log(man.sex)
}
lilei({age: 18,height: 180,sex: 'male'});
在vue 中 常用于给 某个子组件组件实例 获取对应类型
子组件
<!-- MyModal.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const sayHello = () => (console.log('我会说hello'))
defineExpose({
open
})
</script>
父组件
<!-- App.vue -->
<script setup lang="ts">
import MyModal from './MyModal.vue'
const MyModalRef = ref<InstanceType<typeof MyModal> | null>(null)
const handleOperation = () => {
MyModalRef.value?.sayHello()
}
</script>
四、类型使用
1、设定pomise返回值类型
types/verify.ts
export interface IHeader {
code: number
message: string
sid: string
}
export interface IResult {
compress: string
encoding: string
format: string
seq: string
status: string
text: string
}
export interface IPayload {
result: IResult
}
export interface IVerifybody {
header: IHeader
payload: IPayload
}
import type { IVerifybody } from './types/verify';
export function verifyText(text: string) {
return new Promise<IVerifybody>((resolve, reject) => {
request({
url: url as string,
method: "POST",
json: true,
headers: {
"content-type": "application/json",
},
body: {
},
}, function(error, response: request.Response, body) {
if (!error && response.statusCode == 200) {
resolve(body);
}
});
});
}
verifyText('气车行驶的很快').then((data) => {
console.log(111222,data);
const result: string = data.payload.result.text;
console.log(Buffer.from(result,'base64').toString('utf-8'));
});
五、override 修饰符是干嘛的
修饰符的作用:被 override 标示的方法必须得在父类中存在,否则会报错。
class Animal {
getName() { return ''; }
}
class Dog extends Animal {
override bak() {
return 'wang';
}
override getName() {
return 'wang';
}
}
上面这段代码会报错:This member cannot have an ‘override’ modifier because it is not declared in the base class ‘Animal’.就是说重写的放在父类不存在,这样能避免父类重构的时候把一些子类需要重写的方法给去掉。
带有 override 修饰符的方法必须在父类中有对应的声明,否则会报错。
六、TypeScript 忽略变量未使用的报错
// tsconfig.json
{
...
"compilerOptions": {
// noUnusedLocals 设置为 false
"noUnusedLocals": false,
}
}
ts报错
添加上 //@ts-expect-error 或者 // @ts-ignore
//@ts-expect-error
TypeScript 3.9中的新增错误断言。
这个断言比//@ts ignore
更具描述性,因为它不只是忽略下一行,而是显式忽略编译器错误。
ts expect error:意思是这个应该有错误,但我想知道它。如果编译正确,则报告错误。
如果这不再是一个错误,则意味着某些内容已被更改。
// @ts-nocheck
忽略全文;如果你使用这样,需要放在ts的最顶部哈。
七、namespace作用
Namespace 在 TypeScript 中用来解决命名冲突问题的,它可以帮助我们在代码组件化的时候避免命名冲突,同时也可以起到更好的可读性以及明确代码组织结构之类的作用。
它可以定义在全局环境下,也可以定义在模块中。在使用它时我们可以通过 ‘Namspace.’ 的方式去调用它封装的内容。可以说,在 TypeScript 中,Namespace
就相当于C#
的命名空间,Java
的包等。
// 使用 namespace 创建命名空间
export namespace Student {
// 导出 接口
export interface IStudent {
name: string;
age: number;
}
// 导出 类
export class CStudent implements IStudent {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// 导出 函数
export let say = function (who: string, what: string): any {
console.log(who + "说" + what);
};
// 导出 变量
export let other = "变量:我是命名空间导出变量";
}
// 导入命名空间
import { Student } from "./student";
// 调用 接口
let alias: Student.IStudent = {
name: "alias",
age: 18,
};
console.log("接口:", alias);
// 调用 类
let john = new Student.CStudent("john", 28);
console.log("类:", john.name, john.age);
// 调用 函数
Student.say("函数:andy", "hello world");
// 调用 变量
console.log(Student.other);
https://blog.csdn.net/hhtfhtf/article/details/131377479
八、面试题:any、unknown、never类型区别
一、any类型
1、any特点和问题:
1)any 类型表示没有任何限制,该类型的变量可以赋予任意类型的值。
2)对于开发者没有指定类型、TypeScript 必须自己推断类型的那些变量,如果无法推断出类型,TypeScript 就会认为该变量的类型是any,存在安全隐患
3)any类型除了关闭类型检查,还有一个很大的问题,就是它会“污染”其他变量。它可以赋值给其他任何类型的变量(因为没有类型检查),导致其他变量出错。
2、实际开发中,any类型主要适用以下两个场合。
(1)出于特殊原因,需要关闭某些变量的类型检查,就可以把该变量的类型设为any。
(2)为了适配以前老的 JavaScript 项目,让代码快速迁移到 TypeScript,可以把变量类型设为any。有些年代很久的大型 JavaScript 项目,尤其是别人的代码,很难为每一行适配正确的类型,这时你为那些类型复杂的变量加上any,TypeScript 编译时就不会报错。
总之,TypeScript 认为,只要开发者使用了any类型,就表示开发者想要自己来处理这些代码,所以就不对any类型进行任何限制,怎么使用都可以。
从集合论的角度看,any类型可以看成是所有其他类型的全集,包含了一切可能的类型。TypeScript 将这种类型称为“顶层类型”(top type),意为涵盖了所有下层。
二、unkonwn类型
为了解决any类型“污染”其他变量的问题,TypeScript 3.0 引入了unknown类型。它与any含义相同,表示类型不确定,可能是任意类型,但是它的使用有一些限制,不像any那样自由,可以视为严格版的any。
unknown 和 any 的 相同和区别:
1、unknown跟any的相似之处,在于所有类型的值都可以分配给unknown类型。
2、unknown类型跟any类型的不同之处在于,它不能直接使用。主要有以下几个限制。
1)首先,unknown类型的变量,不能直接赋值给其他类型的变量(除了any类型和unknown类型)。避免了污染问题,从而克服了any类型的一大缺点。
let v:unknown = 123;
let v1:boolean = v; // 报错
let v2:number = v; // 报错
2)其次,不能直接调用unknown类型变量的方法和属性。
let v1:unknown = { foo: 123 };
v1.foo // 报错
let v2:unknown = 'hello';
v2.trim() // 报错
let v3:unknown = (n = 0) => n + 1;
v3() // 报错
3)再次,unknown类型变量能够进行的运算是有限的,只能进行比较运算(运算符==、=、!=、!、||、&&、?)、取反运算(运算符!)、typeof运算符和instanceof运算符这几种,其他运算都会报错。
let a:unknown = 1;
a + 1 // 报错
a === 1 // 正确
那么,怎么才能使用unknown类型变量呢?
答案是只有经过“类型缩小”,unknown类型变量才可以使用。所谓“类型缩小”,就是缩小unknown变量的类型范围,确保不会出错。(类型缩小:只有明确unknown变量的实际类型,才允许使用它,防止像any那样可以随意乱用,“污染”其他变量。类型缩小以后再使用,就不会报错)
let s:unknown = 'hello';
if (typeof s === 'string') {
s.length; // 正确
}
总之,unknown可以看作是更安全的any。一般来说,凡是需要设为any类型的地方,通常都应该优先考虑设为unknown类型。
在集合论上,unknown也可以视为所有其他类型(除了any)的全集,所以它和any一样,也属于 TypeScript 的顶层类型。
三、never 类型
never 类型是 TypeScript 中的底层类型。它在以下情况中很好的被使用:
一个从来不会有返回值的函数,即死循环(如:如果函数内含有 while(true) {});
一个总是会抛出错误的函数(如:function foo() { throw new Error(‘Not Implemented’) },foo 的返回类型是 never);
function foo(): never {//永远不会返回结果 // 死循环 while(true) {}
}
function bar(): never {throw new Error()
}
由于不存在任何属于“空类型”的值,所以该类型被称为never,即不可能有这样的值。
let x:never;
x = 1 // 报错
x = 'hello' // 报错:不可能赋给它任何值,否则都会报错
never 类型的特点
never类型的一个重要特点是,可以赋值给任意其他类型。
function f():never {
throw new Error('Error');
}
let v1:number = f(); // 不报错
let v2:string = f(); // 不报错
let v3:boolean = f(); // 不报错
为什么never类型可以赋值给任意其他类型呢?这也跟集合论有关,空集是任何集合的子集。TypeScript 就相应规定,任何类型都包含了never类型。因此,never类型是任何其他类型所共有的,TypeScript 把这种情况称为“底层类型”(bottom type)。