[TOC]
>[success] # 高级类型(二)
>[danger] ##### 多态的this类型
~~~
1.下面案例使用的链式调用,在1.7版本之前的ts这么写,用PowCounter 时候,使用父类的add 和sub
会报错,新版后这种返回this 会'含类或接口的子类型'
~~~
~~~
class Counter{
constructor(public count:number=0){}
add(value:number){
this.count +=value
return this
}
sub(value:number){
this.count -=value
return this
}
}
class PowCounter extends Counter{
pow(value:number){
this.count = this.count ** value
return this
}
}
const powCounter = new PowCounter()
powCounter.add(10).sub(5).pow(2)
~~~
>[danger] ##### 索引类型 -- 查询操作符 (keyof)
~~~
1.'keyof T'的结果为T上已知的公共属性名的联合
~~~
~~~
interface Info {
name:string;
age:number;
}
let info: keyof Info // 相当于let info: "name" | "age"
info = 'name'
info = 'age'
// info = 'w' // 报错
~~~
* 泛型配合类型索引
~~~
1.{1} 说明我们想让'infoValue'返回的都是string 类型的数组,所以age是数字类型因此报错
2.{2} 是我们同过keyof 规定了只能是vauleInfo上存在的key 才行因此'ss' 报错
3.{3} 这个是索引访问操作符的一个用法下面会讲解
~~~
~~~
/ 现在有个一个需求 取出对象对应key 的value 值
// 这是我还不会用ts 时候工作天天写的 没有合理运用正确ts 写法
// function getValue(obj:any,key:any[]){
// return key.map(item=>obj[item])
// }
// 用索引查询累心 和 泛型来写正确写上面的代码逻辑
function getValue<T,K extends keyof T>(obj:T,key:K[]):T[K][]{
{3}
return key.map((item)=> obj[item])
}
const vauleInfo ={
name:'w',
age:18
}
let infoValue = getValue(vauleInfo, ['name', 'age'])
// let infoValue:string[] = getValue(vauleInfo, ['name', 'age']) // 只返回string 类型的,添加age报错
{1}
// getValue(vauleInfo, ['name', 'age','ss']) // 报错valueInfo 属性没有ss {2}
~~~
>[danger] ##### 索引访问操作符
~~~
1.可以像js 对象一样直接取出一个一个字段的类型
~~~
~~~
// 索引访问操作符
interface Info1 {
name: string,
age: number
}
let infos:Info1['name'] // 相当于取出Info1 下角标name字段对应类型 let infos:string
// infos =1 // 报错
infos = 'w'
// 实际应用
// 相当于只能返回的是T对象规定的值类型
function getProperty<T,K extends keyof T>(obj:T,names:K):T[K]{
return obj[names]
}
getProperty({name:'w',age:12},'name') // 返回的类型只能是 string 和数字
~~~
>[danger] ##### keyof 和 索引访问操作
~~~
1.来看这个案例首先keyof 取得是key 字段类型
2.索引取得是这个key对应的value类型
~~~
~~~
interface Dictionary<T> {
[key: string]: T;
}
let keys: keyof Dictionary<number>; // string
let value: Dictionary<number>['foo']; // number
~~~
* 这里是可以通过配置寻该是佛包含 null 和undefined
![](https://img.kancloud.cn/80/79/80792b62d0bccc475c41cd7bee6e19f9_466x62.png)
~~~
interface Type {
a:never,
b:never,
c:null,
d:undefined,
e:string,
f:number,
}
// 取出所有Type 接口对应的value 的类型
type Test = Type[keyof Type]
const valueType:Test = 'w'
~~~
>[danger] ##### 映射类型
~~~
1.Readonly -- 所有字段只读映射ts中分装的形式 :
type Record<K extends keyof any, T> = {
[P in K]: T;
};
2.Partial -- 所有字段可选映射方法ts中分装的形式 :
type Partial<T> = {
[P in keyof T]?: T[P];
};
3.Pick -- 挑选指定字段ts中分装的形式 :
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
4.Record -- 指定key ,value 类型ts中分装的形式 :
type Record<K extends keyof any, T> = {
[P in K]: T;
};
5infer 、Exclude、 Extract、 NonNullable、 ReturnType 、InstanceType具体使用看下面案例(这些后期可以通过看ts实际实现)
6.ts 的'K in Keys' 写法可以理解成js'for .. in',只不过他是针对类型的,使用这些写法简单小案例:
type Keys = 'option1' | 'option2';
type Flags = { [K in Keys]: boolean };
~~~
* 探讨映射类型出现的原因
~~~
1.我们可以用'P in keyof T' 做一个类似循环的字段方式
~~~
~~~
// 一个场景 我们现在有一个接口,在一些情况下这些接口值是只能可读的
interface Info1{
age:number;
name:string;
}
const info1:Info1 ={
age:12,
name:'w'
}
// 现在有另外一个变量和info1有相同的属性但只是他的字段都是只读
interface Info2{
readonly age:number,
readonly name:string,
}
const info2:Info2 ={
age:12,
name:'w'
}
// 现在产生一个问题 这样对这种重复字段只是在使用定义不同的内容每次都要做重复操作
// 先使用泛型和类型别名来做一统一转换的方法
type ReadonlyType<T> ={
readonly [P in keyof T]:T[P]
}
const infos3:ReadonlyType<Info1> = {
age:1,
name:'w'
}
~~~
* ts 提供的只读 和可选 映射方法使用
~~~
interface Info1 {
age: number;
name: string;
}
// 都是只读
const infos3:Readonly<Info1> = {
age:1,
name:'w'
}
// 都是可选
const infos4: Partial<Info1> = {
age: 1,
name: 'w'
}
~~~
* ts 提供的选择方法
~~~
// 这里要注意可选是要填两个位置
interface Info {
name:string,
age:number,
address:string
}
// 只使用 name 和age 字段
const a:Pick<Info,'name'|'age'> = {
name:'w',
age:12
}
// 当方法需要返回是一个对象时候
const info5 = {
name: 'lison',
age: 18,
address: 'beijing',
}
// 返回的是一个对象 用pick 来做了指定
function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const res: any = {}
keys.map((key) => {
res[key] = obj[key]
})
return res
}
const nameAndAddress = pick(info5, ['name', 'address'])
console.log(nameAndAddress)
~~~
* ts Record 记录
* 常见不规范的错
~~~
// 错误用有时候工作,定义了一个对象类型赋值是一个object
// object 没有foo字段会报错
// let bb: object ={
// foo:'s'
// }
// bb.foo;
// k 为任意类型 ,value 为任意类型,解决
let aa: Record<any, any>={
foo:'w'
}
aa.foo
~~~
* 注意Record两个参数指定的都是类型
~~~
interface Info {
name:string,
age:number
}
// 定义的a key 是Info 接口Key类型,value 是string类型
const a:Record<Info[keyof Info],string> = {
name:'w',
age:'w'
}
~~~
* 一个函数使用小案例
~~~
function mapObject<K extends string | number, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U> {
const res: any = {}
for (const key in obj) {
res[key] = f(obj[key])
}
return res
}
const names = { 0: 'hello', 1: 'world', 2: 'bye' }
const lengths = mapObject(names, (s) => s.length)
console.log(lengths)
~~~
* 其他的映射方法
~~~
// infer 推到
type Type8<T> = T extends any[] ? T[number] : T
type Test3 = Type8<string[]>
type Test4 = Type8<string>
type Type9<T> = T extends Array<infer U> ? U : T
type Test5 = Type9<string[]> // type Test5 = string 如果是数组取得是数组向中的值
type Test6 = Type9<string> // type Test6 = string
// Exclude<T, U>
type Type10 = Exclude<'a' | 'b' | 'c', 'a'> // type Type10 = "b" | "c" 排除 a
// Extract<T, U>
type Type11 = Extract<'a' | 'b' | 'c', 'c' | 'b'|'f'> // type Type11 = "b" | "c" 取两者的交集
// NonNullable<T>
type Type12 = NonNullable<string | number | null | undefined> // type Type12 = string | number 排除null 和undefined
// ReturnType<T>
type Type13 = ReturnType<() => string> // type Type13 = string 将方法返回值作为类型
type Type14 = ReturnType<() => void> // type Type14 = void
// InstanceType<T> // 有就是他的构造函数类型没有就是any
class AClass {
constructor() {}
public sum(){}
}
class BClass {
constructor() {}
public sum(){}
}
class DClass {
constructor() {}
}
type T1 = InstanceType<typeof AClass>
type T2 = InstanceType<any>
type T3 = InstanceType<never>
// type T4 = InstanceType<string>
// const t1: T1 = new DClass() // 报错
const t2: T1 = new BClass()
const t3: T1 = new AClass()
const t4: T1 = {sum(){}}
~~~
>[danger] ##### 由映射类型进行推断 ? 这里暂时不懂
~~~
type Proxy<T> = {
get(): T;
set(value: T): void;
}
type Proxify<T> = {
[P in keyof T]: Proxy<T[P]>
}
function proxify<T>(obj: T): Proxify<T> {
const result = {} as Proxify<T>
for (const key in obj) {
result[key] = {
get: () => obj[key],
set: (value) => obj[key] = value,
}
}
return result
}
let props = {
name: 'lison',
age: 18,
}
let proxyProps = proxify(props)
proxyProps.name.set('li')
// console.log(proxyProps.name.get())
// -------------- 揭开包装类型-----------------
function unproxify<T>(t: Proxify<T>): T {
const result = {} as T
for (const k in t) {
result[k] = t[k].get()
}
return result
}
let originalProps = unproxify(proxyProps)
~~~
>[danger] ##### 移除特定修饰符 -- 用'-' 来移除
~~~
// 移除特定修饰符
readonly 和 ?可以选
interface A {
readonly name:string;
readonly age:number;
}
type RemoveA<T> = {
-readonly [P in keyof T]-?:T[P]
}
~~~
>[danger] ##### 不要思维定式
~~~
type Tuple = [number, string, boolean]
type promiseTuple = MapToPromise<Tuple>
let tuple1: promiseTuple = [
new Promise((resolve, reject) => resolve(1)),
new Promise((resolve, reject) => resolve('a')),
new Promise((resolve, reject) => resolve(false)),
]
~~~
>[danger] ##### unknown
~~~
1.TypeScript 3.0 引入了新的unknown 类型,它是 any 类型对应的安全类型。
2.unknown 和 any 的主要区别是 unknown 类型会更加严格:在对 unknown 类型的值执行大多数操作之前,
我们必须进行某种形式的检查。而在对 any 类型的值执行操作之前,我们不必进行任何检查。
3.使用any类型,可以很容易地编写类型正确但是执行异常的代码。如果我们使用 any 类型,
就无法享受 TypeScript 大量的保护机制。但如果能有顶级类型也能默认保持安全呢?这就是 unknown 到来的原因
~~~
~~~
// unknown
// [1] 任何类型都可以赋值给unknown类型
let value1: unknown
value1 = 'a'
value1 = 123
// [2] 如果没有类型断言或基于控制流的类型细化时,unknown不可以赋值给其他类型,此时他只能赋值给unknown和any类型
let value2: unknown
// let value3: string = value2 // 因为value3 是string类型所以 value2不能赋值给value3
value1 = value2 // 可以赋值给any 和unkown 类型
// [3] 如果没有类型断言或基于控制流的类型细化时,不能在他上面进行任何操作
let value4: unknown
// value4 += 1 // 没有给具体指定 因此+=1 报错
// [4] unknown与任何其他类型组成的交叉类型,最后都等于其他类型
type type1 = string & unknown // string
type type2 = number & unknown // number
type type3 = unknown & unknown // unknown
type type4 = unknown & string[] // string[]
// [5] unknown与任何其他类型(除了any是any)组成的联合类型,都等于unknown类型
type type5 = unknown | string // unknown
type type6 = any | unknown // any
type type7 = number[] | unknown // unknown
// [6] never类型是unknown的子类型
type type8 = never extends unknown ? true : false
// [7] keyof unknown 等于类型never
type type9 = keyof unknown // never
// [8] 只能对unknown进行等或不等操作,不能进行其他操作
// value1 === value2
// value1 !== value2
// value1 += value2
// [9] unknown类型的值不能访问他的属性、作为函数调用和作为类创建实例
let value10: unknown
// value10.age
// value10()
// new value10()
// [10] 使用映射类型时如果遍历的是unknown类型,则不会映射任何属性
type Types1<T> = {
[P in keyof T]: number
}
type type11 = Types1<any> // type type11 = {[x: string]: number;}
type type12 = Types1<unknown> // type type12 = {}
~~~
>[danger] ##### 条件类型
~~~
1.向三运运算动态指定类型
~~~
~~~
// T extends U ? X : Y
type Types2<T> = T extends string ? string : number
// let index: Types2<false>
// type TypeName<T> = T extends any ? T : never
// type Type3 = TypeName<string | number>
type TypeName<T> =
T extends string ? string :
T extends number ? number :
T extends boolean ? boolean :
T extends undefined ? undefined :
T extends () => void ? () => void :
object
type Type4 = TypeName<() => void>
type Type5 = TypeName<string[]>
type Type6 = TypeName<(() => void) | string[]>
type Diff<T, U> = T extends U ? never : T
type Test2 = Diff<string | number | boolean, undefined | number>
// keyof T 排除never 类型
type Type7<T> = {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T]
interface Part {
id: number;
name: string;
subparts: Part[];
undatePart(newName: string): void
}
type Test1 = Type7<Part>
~~~
- Vue--基础篇章
- Vue -- 介绍
- Vue -- MVVM
- Vue -- 创建Vue实例
- Vue -- 模板语法
- Vue -- 指令用法
- v-cloak -- 遮盖
- v-bind -- 标签属性动态绑定
- v-on -- 绑定事件
- v-model -- 双向数据绑定
- v-for -- 只是循环没那么简单
- 小知识点 -- 计划内属性
- key -- 属性为什么要加
- 案例说明
- v-if/v-show -- 显示隐藏
- v-for 和 v-if 同时使用
- v-pre -- 不渲染大大胡语法
- v-once -- 只渲染一次
- Vue -- class和style绑定
- Vue -- filter 过滤器
- Vue--watch/computed/fun
- watch -- 巧妙利用watch思想
- Vue -- 自定义指令
- Vue -- $方法
- Vue--生命周期
- Vue -- 专属ajax
- Vue -- transition过渡动画
- 前面章节的案例
- 案例 -- 跑马灯效果
- 案例 -- 选项卡内容切换
- 案例-- 筛选商品
- 案例 -- 搜索/删除/更改
- 案例 -- 用computed做多选
- 案例 -- checked 多选
- Vue--组件篇章
- component -- 介绍
- component -- 使用全局组件
- component -- 使用局部组件
- component -- 组件深入
- component -- 组件传值父传子
- component -- 组件传值子传父
- component -- 子传父语法糖拆解
- component -- 父组件操作子组件
- component -- is 动态切换组件
- component -- 用v-if/v-show控制子组件
- component -- 组件切换的动画效果
- component -- slot 插槽
- component -- 插槽2.6
- component -- 组件的生命周期
- component -- 基础组件全局注册
- VueRouter--获取路由参数
- VueRouter -- 介绍路由
- VueRouter -- 安装
- VueRouter -- 使用
- VueRouter--router-link简单参数
- VueRouter--router-link样式问题
- VueRouter--router-view动画效果
- VueRouter -- 匹配优先级
- vueRouter -- 动态路由
- VueRouter -- 命名路由
- VueRouter -- 命名视图
- VueRouter--$router 获取函数
- VueRouter--$route获取参数
- VueRouter--路由嵌套
- VueRouter -- 导航守卫
- VueRouter -- 写在最后
- Vue--模块化方式结构
- webpack--自定义配置
- webpack -- 自定义Vue操作
- VueCli -- 3.0可视化配置
- VueCli -- 3.0 项目目录
- Vue -- 组件升级篇
- Vue -- 组件种类与组件组成
- Vue -- 组件prop、event、slot 技巧
- Vue -- 组件通信(一)
- Vue -- 组件通信(二)
- Vue -- 组件通信(三)
- Vue -- 组件通信(四)
- Vue -- 组件通信(五)
- Vue -- 组件通信(六)
- Vue -- bus非父子组件通信
- Vue -- 封装js插件成vue组件
- vue组件分装 -- 进阶篇
- Vue -- 组件封装splitpane(分割面板)
- UI -- 正式封装
- Vue -- iview 可编辑表格案例
- Ui -- iview 可以同时编辑多行
- Vue -- 了解递归组件
- UI -- 正式使用递归菜单
- Vue -- iview Tree组件
- Vue -- 利用通信仿写一个form验证
- Vue -- 使用自己的Form
- Vue -- Checkbox 组件
- Vue -- CheckboxGroup.vue
- Vue -- Alert 组件
- Vue -- 手动挂载组件
- Vue -- Alert开始封装
- Vue -- 动态表单组件
- Vue -- Vuex组件的状态管理
- Vuex -- 参数使用理解
- Vuex -- state扩展
- Vuex -- getters扩展
- Vuex--mutations扩展
- Vuex -- Action 异步
- Vuex -- plugins插件
- Vuex -- v-model写法
- Vuex -- 更多
- VueCli -- 技巧总结篇
- CLI -- 路由基础
- CLI -- 路由升级篇
- CLI --异步axios
- axios -- 封装axios
- CLI -- 登录写法
- CLI -- 权限
- CLI -- 简单权限
- CLI -- 动态路由加载
- CLI -- 数据性能优化
- ES6 -- 类的概念
- ES6类 -- 基础
- ES6 -- 继承
- ES6 -- 工作实战用类数据管理
- JS -- 适配器模式
- ES7 -- 装饰器(Decorator)
- 装饰器 -- 装饰器修饰类
- 装饰器--修饰类方法(知识扩展)
- 装饰器 -- 装饰器修饰类中的方法
- 装饰器 -- 执行顺序
- Reflect -- es6 自带版本
- Reflect -- reflect-metadata 版本
- 实战 -- 验证篇章(基础)
- 验证篇章 -- 搭建和目录
- 验证篇章 -- 创建基本模板
- 验证篇章 -- 使用
- 实战 -- 更新模型(为了迎合ui升级)
- 实战 -- 模型与接口对接
- TypeSprict -- 基础篇章
- TS-- 搭建(一)webpack版本
- TS -- 搭建(二)直接使用
- TS -- 基础类型
- TS -- 枚举类型
- TS -- Symbol
- TS -- interface 接口
- TS -- 函数
- TS -- 泛型
- TS -- 类
- TS -- 类型推论和兼容
- TS -- 高级类型(一)
- TS -- 高级类型(二)
- TS -- 关于模块解析
- TS -- 声明合并
- TS -- 混入
- Vue -- TS项目模拟
- TS -- vue和以前代码对比
- TS -- vue简单案例上手
- Vue -- 简单弄懂VueRouter过程
- VueRouter -- 实现简单Router
- Vue-- 原理2.x源码简单理解
- 了解 -- 简单的响应式工作原理
- 准备工作 -- 了解发布订阅和观察者模式
- 了解 -- 响应式工作原理(一)
- 了解 -- 响应式工作原理(二)
- 手写 -- 简单的vue数据响应(一)
- 手写 -- 简单的vue数据响应(二)
- 模板引擎可以做的
- 了解 -- 虚拟DOM
- 虚拟dom -- 使用Snabbdom
- 阅读 -- Snabbdom
- 分析snabbdom源码 -- h函数
- 分析snabbdom -- init 方法
- init 方法 -- patch方法分析(一)
- init 方法 -- patch方法分析(二)
- init方法 -- patch方法分析(三)
- 手写 -- 简单的虚拟dom渲染
- 函数表达解析 - h 和 create-element
- dom操作 -- patch.js
- Vue -- 完成一个minVue
- minVue -- 打包入口
- Vue -- new实例做了什么
- Vue -- $mount 模板编译阶段
- 模板编译 -- 分析入口
- 模板编译 -- 分析模板转译
- Vue -- mountComponent 挂载阶段
- 挂载阶段 -- vm._render()
- 挂载阶段 -- vnode
- 备份章节
- Vue -- Nuxt.js
- Vue3 -- 学习
- Vue3.x --基本功能快速预览
- Vue3.x -- createApp
- Vue3.x -- 生命周期
- Vue3.x -- 组件
- vue3.x -- 异步组件???
- vue3.x -- Teleport???
- vue3.x -- 动画章节 ??
- vue3.x -- 自定义指令 ???
- 深入响应性原理 ???
- vue3.x -- Option API VS Composition API
- Vue3.x -- 使用set up
- Vue3.x -- 响应性API
- 其他 Api 使用
- 计算属性和监听属性
- 生命周期
- 小的案例(一)
- 小的案例(二)-- 泛型
- Vue2.x => Vue3.x 导读
- v-for 中的 Ref 数组 -- 非兼容
- 异步组件
- attribute 强制行为 -- 非兼容
- $attrs 包括 class & style -- 非兼容
- $children -- 移除
- 自定义指令 -- 非兼容
- 自定义元素交互 -- 非兼容
- Data选项 -- 非兼容
- emits Option -- 新增
- 事件 API -- 非兼容
- 过滤器 -- 移除
- 片段 -- 新增
- 函数式组件 -- 非兼容
- 全局 API -- 非兼容
- 全局 API Treeshaking -- 非兼容
- 内联模板 Attribute -- 非兼容
- key attribute -- 非兼容
- 按键修饰符 -- 非兼容
- 移除 $listeners 和 v-on.native -- 非兼容
- 在 prop 的默认函数中访问 this -- ??
- 组件使用 v-model -- 非兼容
- 渲染函数 API -- ??
- Slot 统一 ??
- 过渡的 class 名更改 ???
- Transition Group 根元素 -- ??
- v-if 与 v-for 的优先级对比 -- 非兼容
- v-bind 合并行为 非兼容
- 监听数组 -- 非兼容