前置:
如何使用vscode调试ts?
- webpack+babel(常用)
- Vite(常用)
- tsc(typescript compiler)
类和类型:
JS中,类型 就是数据种类的区分,在JS中有八种数据类型
类 针对对象类型object 进行分类
ts类型标明一般首字母大写,除了特定的几个情况如unkown/()=>{}等
ts类型:在js八种类型的基础上加上 元组、any、never
(object类型基本不用,因为object类型种包含更具体的内容,写object没有什么意义)
ts面试题集:
any、unknown、never 区别
any 和 unkown 顶级类型
两者都是顶级类型,任何类型的值都可以赋值给顶级类型
//不报错,从右到左兼容
let foo:any = 123
let bar:unkown = 123
const value:unkown = 'npc'
const someString:string = value //报错,value为unkown类型不可兼容其他
const value:any = 'npc'
const someString:string = value //不报错,any不做任何类型检查
//类型收窄
const value:unkown = 'npc'
const someString:string = value as string //不报错,先把unkown收窄为string再赋值即可兼容
//通过断言让类型收窄
never 底类型
不应该出现的类型,一般出现了则表示此处代码出错
interface Foo {
type:'foo'
}
总结
- 顶类型可以被赋值为任何东西,底类型不可以被赋值为任何东西
- unkown比any类型检查更为严格,一般使用unkown需要结合断言做类型收窄
any 没有任何限制(永远都不知道它是什么类型)
unkown 声明的时候不知道它是声明类型,要使用的时候需要明确指定类型(现在不知道它是什么类型,但使用它的时候就得知道它是什么类型)
type c = {name:111}
let b:unkown = JSON.parse('name':'npc')
console.log(b as c).name
never 表示空集,什么都没有,不应该存在的类型,代码出现never表示此处代码出错
type Dir = 1|2|3|4|undefined
let dir:Dir
switch(dir){
case 1:
break
case 2:
break
case 3:
break
case 4:
break
case undefined:
break
default:
console.log(dir)
break
}
type X = number & string //交集为空,即never
void 用于函数返回值为undefined、null或者无return,简单说void就是undefined
let print:()=>void = function (){
console.log(1)
return undefined
}
type 和 interface 区别
- 组合方式:interface使用extends来实现继承;type 使用 & 来实现联合类型
- 扩展方式:interface 可以重复声明和添加新字段用来扩展;type一个类型只能声明一次,之后后无法更改
- 范围不同:type 适用于基本类型和对象类型,interface 一般用于对象类型
- 命名方式:interface 会创建新的类型名,type 只是类型别名,即给类型取了个别名,并没有新创建类型
interface B {
b:string
}
interface A extends B {
a:stirng
}
const a:A = {
a:'hi'
b:'npc'
}
//等同于
type BB ={
bb:string
}
type AA ={
aa:string
} && BB
const aa:AA ={
aa:'hi'
bb:'npc'
}
//1.js
interface A {
a:string
}
//2.js
interface A {
a:string
}
//3.js
const a:A = { //1.js和2.js两个interface会自动合并,即可扩展
a:'hi'
b:'npc'
}
//1.js
type A = {
a:string
}
//2.js
type A ={
a:string //报错,不可重复声明,即不可扩展,不可变,类似const声明
}
type UserName = string
interface UserName extends string {}//报错
type X = number
const x:X = 1
type Y = typeof x // type Y = number ,X只是number类型的别名,没有真正存在
//可以使用类型别名为任何类型指定名称,而不仅仅是对象类型
type A = typeof x //熟记此处 typeof 用法
注意:接口interface可以定义任意key
export interface Ilist {
product_id:string,
...
[propname:string]:any //以防后端从新增字段不同于上面
}
泛型
广泛的类型,当作函数去理解
泛型就是给类型传参,传入的参数就是“泛型变量/类型变量”**构造函数<描述构造函数>**
const a:Array<number> = [1,2,3]
//类比JS
type A = 'hi' | 123 //TS
let a =['hi',123] //JS
let fn = (x:number) => x+1
type F<T> = T | T[]
type FNumber = F<number>
type FString = F<string>
type Add<T> = (a:T,b:T)=>T //定义
const addN:Add<number> = (a,b)=> a+b //调用
const addS:Add<stirng> = (a,b)=> a+ '' + b
number[] === Array<number>
//Array的底层,实际上是interface定义的函数
interface Array<T> {
[n:number]:T
}
注意:泛型可以嵌套泛型,也可以传多个参
元组
用于固定数组的长度,需要指定位置指定每个元素的类型
let p:[number,number] = [100,200]
p = [1,2,3,4] //报错
p = [1,2]
console.log(p as any).push(3)// 强制修改,使用断言
枚举
很少使用
//不推荐写法
enum Dir2 {东,南,西,北}
let d:Dir2 = Dir2.东
let d2:Dir2 = Dir2.西
console.log(d)
//推荐以下写法
type Dir = '东'|'南'|'西'|'北'
type Dir3 = 0|1|2|3
let dir:Dir = '东'
let dir2:Dir = '南'
断言
联合类型| 和 交叉类型&(类型体操)
联合类型|
联合了 又如何做区分?
type A ={
name:'a';
age:number
}
type B ={
name:string;
gender:string
}
//情况一:入参为简单数据类型可以用typeof做类型区分
const f = (n:number|B)=>{
if(typeof n === 'number'){ //类型收窄(类型可收窄至具体某值)
n.toFixed()
}else{ //类型推测
n.name
}
}
//情况二:入参为复杂数据类型 需要找到共同key做类型分支
const f = (n:A|B)=>{
if(n.name === 'a'){ //类型区分(俩类型至少有一个相同key)
}else{
}
}
交叉类型&
不能用于简单数据类型,因为简单数据类型是没有交集的,则会显示never
一般用于object,但需要具体描述object
表示相加
怎么声明div的类型
如果项目种tsconfig.json文件声明了”lib”:[“DOM”],ts自带类型模糊匹配,自带很多内置的复杂类型
const div1:HTMLdivElement
TS工具类型 Partial、Required、Readonly、Exclude、Extract、Omit、ReturnType 的作用和实现
partial 部分类型
Required 必填类型
针对对象类型的key
Pick 挑选类型
Omit 排除key类型
针对简单类型
Extract 提取类型
Exclude 排除类型
Readonly 只读类型
ReturnType 返回值类型
Record 记录key和value类型
interface User {
id:stirng;
name:string;
}
const user:User = { //报错,因为user上传至服务器才有id,除了?可选,还有其他解决?
name:'npc'
}
//改为Partial,变为User的一部分,即User类型的每个key都是可选的
const user:Partial<User> = {
name:'npc'
}
//和Partial 作用相反
const user:Required<User> = {
name:'npc' //报错,id必填
}
interface User {
id:string;
name:string;
age:number
}
type God = Pick<User,'id'|'name'> //挑选
//从User类型中挑选出id和name两个子类型构成God类型
type God = Omit<User,'id'> //排除的是key 引用
type Dir = '东'|'南'|'西'|'北'
type Dir2 = Exclude<Dir,'北'>
type Dir3 = Extract<Dir,'东'|'南'>//作用相反,有南则提取,没有则不提取
function f(a:number,b:number){
return a+b
}
type A = ReturnType<typeof f> //泛型中必须传类型typeof f,而不是传值f
type a = Record<string,number> //a类型的所有key为stirng,所有value为number
//且key类型只能有string|number|symbol
as const类型收窄
一旦声明后续不可强制再变,如改为any类型后再push
const a = [1,2,3] //类型推测 a:number[]
const a = [1,2,3] as const //类型推测 a:readonly [1,2,3] 只读的,收窄至静态常量
tips:查看这些工具函数源码,相当精炼
如何给不同数据加type
typeof两个bug
- typeof null 返回字符串object
- typeof fn 返回字符串functuon
如果遇到object类型,使用具体的class进行描述,即Array 和()=> ?(不能写Function)
class是类型type还是值value?
答案全都是,class既是类型又是值
class A {
}
//const x:类型 = 值
const B = A // class A是值(对于JS来说)
const a:A = new A() //class A 是类型(对于TS来说)