几个提高开发效率的 TS 小技巧
有时候你满脑子疑惑“哈?这都能报错?我写的明明是对的啊”
来看看下面的小技巧吧,总有一个能提高你的效率
提取元组或数组类型中每一项的类型
type ArrayType1 = Array<{
a: number
b: number
type ArrayType2 = ({
a: number
b: number
} | {
c: string
d: string
// 通过索引访问来获取,我们都知道数组的索引是 number 类型的
type GetArrayOrTupleItemType1 = ArrayType1[number]
// 得到
type GetArrayOrTupleItemType1 = {
a: number
b: number
// 通过 infer 进行推导
type GetArrayOrTupleItemType2 = ArrayType2 extends Array<infer U> ? U : never
// 得到
type GetArrayOrTupleItemType2 = {
a: number
b: number
} | {
c: string
d: string
获取接口 interface (或对象类型)中的类型
interface A {
b: string
c: number
d: Array<{
e: symbol
type B = A['b']
type C = A['c']
// 与上一小节的技巧配合使用
type E = A['d'][number]['e']
const 和 let 类型推导的区别
这在 ts 中叫做 类型推断
const 声明的常量的类型为字面量类型
const a = 1 // 则 a 的类型就为 1
const d = '2' // 则 a 的类型就是 '2'
// 这是因为常量是不可修改的,所以不会进行类型推断,但下面的情况不是这样
// b 的类型为 number,c 的类型为 string,这是由于右侧得到的类型即为 (number | string)[]
const [b, c] = [1, '2']
// d.e 的 类型 为 number,d.f 的类型 为 string,因为常量对象的属性是可以进行操作的,类型推导也会发生在初始化成员(对象属性)的过程中
const d = {
e: 1,
f: '2'
let 声明的变量时,类型会被推导
let a = 1 // a 为 number 类型
let b = '2' // b 为 string 类型
as const 进行断言
上一小节中 let 声明的 a 和 b 如何得到它们初始值本身对应的字面量类型?
let a: 1 = 1 // 1 本身也可以作为类型使用
// 同样的也可以进行断言
let a = 1 as 1
// 也可以用 as const 更加统一,明了
let a = 1 as const
let b = '2' as const
let c = {
e: 1 as const
let d = [1 as const]
常用于接收类型为字面量类型的情况,如
const a = {
b: 1
const c = (params: { b: 1 }) => {}
c(a) // 报错:不能将类型“number”分配给类型“1”,在 b: 1 那一行最后加上 as const 即可解决
as unknown as xxx 代替 as any
开发中经常会遇到类型定义的不太好,需要用 as 进行断言的情况,简单来看,可以直接用 as any 解决几乎所有的 ts 类型问题(如报错)
但不利于后续的维护,维护者可能并不知道被 as any 的目标应该是什么类型,正确的做法应该是用 as unknown as xxx 代替,这样能看到明确的类型,如
// 真实场景可能更为复杂
;(window as unknown as { handler: (() => void) | null }).handler = null
非空断言符
有时候定义了某个变量或对象属性会包含 undefined 或 null 类型,但使用时逻辑上一定不存在未定义的情况
那么就可以使用非空断言
let a: undefined | {
b: string
} = undefined
function c() {
a = {
b: '1'
console.log(a.b) // 提示:对象可能为“未定义”
// 在可能为 undefined 或 null 的变量或对象属性后增加 ! 非空断言操作符
console.log(a!.b)
可选链操作符
大部分情况下,可选链操作符 ?. 会被编译,如果它前面的变量或对象属性为空,则会直接返回 undefined,新版 Chrome 浏览器已经支持这一特性,可以直接使用
declare 关键字
declare 用于声明那些当前模块下没有,但实际上可以被访问的变量常量或方法等
declare const a: string
declare function b(): void
declare class C {}
// 这样使用常量 a 和函数 b 都不会报错
// 比如有个 jquery 对象是通过 script 形式引入的,那么在项目的 .d.ts 文件中这样写,可以让它在任何地方都能被访问
declare const $: JQueryStatic
// 当然,declare module 'jquery' 也是可以的
有限元素的数组(元组)类型声明以及某一项(或多项)可为 undefined 的情况
type DateArray = [Date, Date]
// 可为空
type DateArray1 = [Date | undefined, Date]
// 或直接在可为空的元素后面加 ?
type DateArray2 = [Date?, Date?]
函数类型中的参数关系限定
/* 如果 a 为 1 的时候,b 必须 传 '1',a 为 2,b 必须传 2 */
/* 这样写无法限定两者的关系 */
function a(a: 1 | 2, b: '1' | '2') {
// xxx
/* 可以利用重载 */
function a(a: 1, b: '1'): void
function a(a: 2, b: '2'): void
function a(a: 1 | 2, b: '1' | '2'): void {}
/* 也可以利用泛型 */
interface BType {
1: '1'
2: '2'
/* 只有在泛型中使用 extends 约束,并且 a 和 b 确实有某种关系,才能在 a 为特定类型的时候,约束 b 的类型 */
function b<AType extends keyof BType>(a: Atype, b: BType[AType]): void {}
/* 或 */
function b<AType extends keyof BType>(a: Atype, b: AType extends '1' ? BType['1'] : BType['2']): void {}
* 你也可以用上述两种方式来声明参数与返回值的对应关系,泛型与 extends 的特性也可以用于类的构造器的声明
* 注意,如果函数只有一个参数,你也可以这样写
function c({ a, b }: { a: 1; b: '1' } | { a: 2; b: '2' }): void {}
如何得到异步函数返回值的具体类型?
declare function a(): Promise<{
b: 1
c: '2'
async function b() {
// 也就是这个 c 的类型,不调用 a 的情况如何获取呢?
const c = await a()
// 先能获得 Promise 泛型中传入的的类型
type GetPromiseType<P extends unknown> = P extends Promise<
infer Params
? Params
// 这样就能得到了(ReturnType 是自带的工具类型)
type GetAsyncFunctionReturnType = GetPromiseType<ReturnType<typeof a>>
提示找不到具有类型为 "string" 的参数的索引签名
const a = {
b: 1,
c: 2
let c: string = 'b'
// 元素隐式具有 "any" 类型,因为类型为 "string" 的表达式不能用于索引类型 "{ b: number; c: number; }"。
// 在类型 "{ b: number; c: number; }" 上找不到具有类型为 "string" 的参数的索引签名。
const d = a[c] // 这一行报错
// 这样定义 a 就好了
const a: { [key: string]: number } = {
b: 1,
c: 2
// 如果你的 a 已声明类型了
interface A {
b: number
c: number
const a: A = {
b: 1,
c: 2
// 那加在后面也是可以的
const a: A & { [key: string]: number } = {
b: 1,
c: 2
各种自带的实用工具类型
参考我的这篇文章 深入浅出TS的 Utility Types
得到对象类型值类型的联合类型
interface A {
a: 1
b: 2
c: '3'
// 要得到 1 | 2 | '3'
type Values = A[keyof A]
如何重新导出一个类型声明
// a.ts 文件
export interface A {
a: 1
b: '2'
// index.ts 文件
// 方式一,重新执行类型别名
import { A } from './a.ts'
export type AType = A
// 方式二