📕
余烬的小册
数据结构与算法GitHub
  • 总述
  • 经验记录
    • 经验总结
      • web component
      • 前端性能优化总结与分析
      • 我的长列表优化方案
      • 双向通讯解决方案
      • 🔧基于istanbul实现代码测试覆盖率工具
      • 表单系统(低代码表单)
      • 跨端小程序
      • 设计一个即时聊天功能
      • 跨页面通讯 3658699fe4cb4d0bbe22b0881390bacd
    • 踩坑记录
      • HTML踩坑记录
      • Flutter踩坑记录
      • CSS踩坑记录
  • 源码解析
    • Vue源码解析
      • Vue2源码解析系列-响应式原理
      • Vue2源码解析系列-模板编译
      • Vue2源码解析系列-渲染系统(待更新)
        • Patch
      • Vue2源码解析系列-调度系统(todo)
      • Vue2组件更新流程(todo)
      • 如何学习Vue源码
      • Vue3源码解析系列-响应系统
      • Vue3源码解析系列-渲染系统
      • Vue3源码解析系列-组件化和渲染优化(todo)
      • Vue router源码解析(todo)
    • React源码解析(todo)
    • 微前端
      • qiankun源码解析(todo)
    • Vite源码解析
      • Vite Client源码
      • Vite Server源码(todo)
  • 前端技术
    • javaScript
      • ES6
        • 变量声明
        • 模块化
        • 箭头函数
        • 你不知道的for...of
        • 新的数据结构Set和Map
        • JavaScript异步编程终极解决方案
        • ES6 Class 3a0c0a225a534984aabe9a943c5df975
      • JavaScript Error
      • JavaScript浅拷贝和深拷贝
      • JavaScript闭包
      • JavaScript最佳实践
      • JavaScript设计模式
      • async函数的polyfill
    • 深入理解JavaScript系列
      • JavaScript中的继承
      • JavaScript原始类型和引用类型
      • JavaScript浅拷贝和深拷贝
      • JavaScript手写系列
      • JavaScript之this
      • 词法环境和环境记录
      • JavaScript内存泄漏
      • 执行上下文
      • 从ECMAScript规范中学习this
    • TypeScript
      • TypeScript基础教程
      • Typescript高级操作
      • TypeScript工具类型
      • Typescript手写实现工具类型
      • Typescript总结(思维导图)
    • 浏览器原理
      • 页面渲染原理
      • 浏览器存储
      • JavaScript事件循环
      • 事件循环
      • 跨域
      • DOM事件流
      • 从输入url到页面渲染
      • 判断节点之间的关系及根据节点关系查找节点
      • history API
    • 跨端技术
      • Flutter
        • Flutter布局组件
    • 前端工程化
      • Babel插件开发指南
      • 循环依赖
      • pm2
    • React
      • React 状态管理
      • React组件通讯
      • Redux入门
      • Flux
      • React Hook(todo)
      • Effect
  • 服务器端
    • 计算机网络
      • 应用层
      • 运输层
      • 物理层
      • 数据链路层
      • HTTP缓存
      • HTTPS
      • 网络层
    • NodeJs
      • Node.js
      • nodejs最佳实践
      • 《深入浅出Nodejs》小结
      • mongoose填充(populate)
      • node事件循环
      • Node子进程
      • nestjs从零开始
      • nodejs流
      • Nodejs调试
      • Koa源码解析
    • 服务器
      • 操作系统
      • Linux
      • nginx常用指令
      • nginx常用配置
    • 数据库
      • Mysql常见语法
      • MongoDB Indexes索引
  • 前端安全与性能优化
    • 前端安全
      • 跨站脚本攻击(XSS)
      • 跨站点请求伪造(CSRF)
      • 点击劫持
      • 中间人攻击
      • 越权攻击与JWT
    • 前端性能优化
      • 前端监控系统
      • 前端性能优化总结与分析 7348bba0918645b1899006dc842a64c1
      • 衡量性能的核心指标 0dc15ef127cf4f4a9f1137c377420292
      • 图片懒加载
  • 杂项
    • 其他
      • Git
      • web component框架
      • 实现滚动框的懒加载
      • Stencil指南
    • CSS
      • 定位和层叠上下文
      • BFC
      • 盒模型
      • css选择器
      • css变量
由 GitBook 提供支持
在本页
  • keyof
  • typeof
  • 索引访问类型(Indexed Access Types)
  • Mapped Types
  • 条件类型
  • infer
  • 类型谓词
  • 非空断言
  • 确定赋值断言
  • 实用类型
在GitHub上编辑
  1. 前端技术
  2. TypeScript

Typescript高级操作

tags: TypeScript Created time: December 24, 2022 8:42 PM emoji: https://www.typescriptlang.org/favicon-32x32.png?v=8944a05a8b601855de116c8a56d3b3ae

keyof

keyof操作符的作用是遍历一个对象/接口获取键值。

type Point = { x: number; y: number };
type P = keyof Point;
// 相当于
// type P = 'x' | 'y'

const p1:P = 123 // 报错
const p2:P = '888'  // 报错
const p3:P = 'x' // 正确

如果使用索引类型

type Point = { [name: string]: number };
type P = keyof Point;
// 相当于
// type P = number

typeof

在js中typeof主要作用是判定变量类型,而ts扩展了typeof关键字的作用,使其可以进行“读取类型”操作。

const a= 123
const b: typeof a = 123

这对于基本类型不是很有用,但与其他类型操作符结合使用,可以使用typeof方便地表示许多模式。例如,让我们从查看预定义的类型ReturnType开始。它接受函数类型并生成其返回类型:

function fn():string{
    return '123'
}

type fnType = typeof fn;  // type fnType = () => string
let a: ReturnType<fnType>;
a = '123'  // string

为了避免混乱,ts限制typeof关键字后面只能跟标识符(变量名、函数名等)或对象属性。

// 错误
function fn():string{
    return '123'
}
let a:typeof fn() = '123'

// 正确
let o = {
    a: '123'
}
let a: typeof o.a = '123'

索引访问类型(Indexed Access Types)

ts可以通过索引访问类型。可以通过type、interface或对象来设置类型。

// 对象
let o = {
    a: '123'
}
let a: typeof o['a'] = '123'

// 接口
interface O1 {
    a: string
}
type Oa = O1['a']

// 类型别名
type O2 = {
    a: string
}
type Ob = O2['a']

甚至你还可以使用联合类型。

type A = {
    a: string,
    b:number
}
type B = A['a' | 'b'] // string | number
type C = A[keyof A] // string | number

Mapped Types

有些时候你可能要重复定义某些类型,如果你不想编写重复的代码,可以使用映射类型。

type A<Type> = {
    [Property in keyof Type]: boolean;
};

type = {
    a: string;
    b: number;
};
 
type O = A<FeatureFlags>; // {a:boolean;b:boolean}

这里使用到了泛型, [Property in keyof Type] 会迭代FeatureFlags 所有的key,你也可以让所有的key定义为原来的类型。

type A<Type> = {
    [Property in keyof Type]: Type[Property];
};

注意:在映射类型中不能定义属性和方法。

type A<Type> = {
    [Property in keyof Type]: Type[Property];
		a: string; // 错误
		fn:()=>void; // 错误
};

条件类型

很多时候我们需要根据输入值的类型来判断输出值的类型,这个时候就可以使用条件类型。

条件类型的写法有点类似于 JavaScript 中的条件表达式(condition ? trueExpression : falseExpression ):

SomeType extends OtherType ? TrueType : FalseType;

例如:

type A = number;
type B = A extends number ? number : string
const b:B = 1

条件类型配合泛型时就很有用了,例如我们需要实现一个函数,如果传入的参数是字符串则返回string,如果参数是数字类型则返回number,如果使用泛型+条件类型就可以这样写:

function fn<O extends string | number>(arg: O):O extends string ? 'string' : 'number'{
    if(typeof arg === 'string'){
        return 'string' as O extends string ? 'string' : 'number'
    }else {
        return 'number' as any;
    }
}

可以将条件类型提取出来:

type FnReturnType<O extends string | number> = O extends string ? 'string' : 'number'

function fn<O extends string | number>(arg: O): FnReturnType<O> {
    if(typeof arg === 'string'){
        return 'string' as FnReturnType<O>;
    }else {
        return 'number' as FnReturnType<O>;
    }
}

fn<string>('1')

infer

infer需要搭配条件类型来使用,**infer的作用是从正在比较的类型中推断类型,然后在 true 分支里引用该推断结果。**

我们用下面这个例子来描述infer的作用,在下面这个例子中,我们实现ArrayItemType类型来获取数组元素的类型,如果T是一个数组则返回其元素的类型,否则返回null,我们可以这样写:

type ArrayItemType<T> = T extends any[] ?  T[number] : null;

const arr = [1];
const a: ArrayItemType<typeof arr> = 0;

这里运用了索引访问类型,如果使用infer,代码还能更简洁。

type ArrayItemType<T> = T extends Array<infer K> ?  K : null;

const arr = [1];
const a: ArrayItemType<typeof arr> = 0;

注意,infer推断的类型只能在条件类型的true分支中使用,不能在false分支使用,例如下面的代码:

// 正确的
type ArrayItemType<T> = T extends Array<infer K> ?  null : K;

// 错误的,K不能在false分支使用
type ArrayItemType<T> = T extends Array<infer K> ?  null : K;

类型谓词

TypeScript的类型谓词是一个检查类型的函数,用于检查一个输入值是否为某个特定类型。它的语法如下:

function isString(type: unknown): type is string {
    return typeof type === 'string'
}

类型谓词的作用在于它能够检查一个值是否为某个特定类型,并且如果检查成功,返回true,否则返回false。它可以用于类型检查,以确保代码的正确性。

function fn(x: unknown) {
    if (isString(x)  /*判断x是否是string类型*/ ) {
        x.toUpperCase();
    }
}

非空断言

Typescript还有个很实用的操作符——非空断言,它想到于告诉Typescript这个变量肯定不会是undefined或null。

它的用法是变量后面跟个!符号。

例如:

let a: string|null;
let b:string = a // error,因为a可能null
let c:string = a! // ok

也可以用在对象的属性或方法上,就像可选链一样:

a!.b // a不为null或undefined

例如在vue的$refs中,有时我们明确知道ref引用了某个dom节点,例如this.$refs.linkRef,但是typescript会认为this.$refs.linkRef 有可能为undefined,这个时候就可以用非空断言,如this.$refs!.linkRef ,当然另一种写法是用可选链,因为在某些情况下(例如页面跳转)引用的dom节点确实会为空。

确定赋值断言

TypeScript 的确定赋值断言,允许在实例属性和变量声明后面放置一个 ! 号,从而告诉 TypeScript 该属性会被明确地赋值。

let name!: string;

举个例子:

let a:string;

fn()

a.toLocaleLowerCase()

function fn(){
    a = ''
}

在这个例子中,我们已经知道变量a已经被赋值,但是typescript没有检测到,因此我们需要用确定赋值断言明确地告诉

实用类型

typescript提供一些全局范围可用的类型,可以简化我们的一些操作。

上一页TypeScript基础教程下一页TypeScript工具类型

https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type