关键字与语法
类型层全部的"语法关键字"。数量很少——背熟之后看任何库源码都不会有生词。
取与查
keyof关键字取类型的所有键,返回字面量联合。
ts
interface Todo {
title: string
done: boolean
}
type K = keyof Todo
type A = keyof any
带索引签名时会多出 number:keyof { [k: string]: number } → string | number。
typeof关键字把值提升成类型,是值层通往类型层的唯一入口。
ts
const cfg = { host: 'a.com', port: 443 } as const
type Cfg = typeof cfg
type Host = typeof cfg['host']
索引访问 T[K]语法类型层的查表。K 是联合时得到值的联合。
ts
interface Todo {
title: string
done: boolean
}
type A = Todo['title']
type B = Todo['title' | 'done']
type C = [1, 2, 3]['length']
type D = string[]['length']
推导与遍历
infer关键字在条件类型的 extends 子句里声明待推断变量——类型层的解构。
ts
type Return<T> = T extends (...a: any[]) => infer R ? R : never
type First<T extends unknown[]> = T extends [infer F, ...infer _] ? F : never
type Unwrap<T> = T extends Promise<infer U> ? U : T
type A = First<[1, 2, 3]>
TS 4.8+ 支持带约束:infer R extends string。
in(映射类型)关键字类型层唯一的循环。
ts
type Obj<K extends string> = { [P in K]: number }
type A = Obj<'a' | 'b'>
type Nullable<T> = { [K in keyof T]: T[K] | null }用在元组上会保持长度。
as(键重映射)TS 4.1改写键名;返回 never 可丢弃该键。
ts
type Getters<T> = {
[K in keyof T as `get${Capitalize<K & string>}`]: () => T[K]
}
type A = Getters<{ name: string }>
修饰与断言
as const语法把字面量收窄到最窄并加 readonly。
ts
const a = { x: 1, y: 'p' }
const b = { x: 1, y: 'p' } as const
type TA = typeof a
type TB = typeof b
satisfiesTS 4.9校验但保留推导结果,解决标注擦宽 / 断言不校验的两难。
ts
const r = { home: '/', about: '/about' } satisfies Record<string, string>
type H = typeof r.home
与 as const 组合时顺序固定:as const satisfies T。
修饰符 + / -语法ts
type Partial<T> = { [K in keyof T]?: T[K] } // 加 ?
type Required<T> = { [K in keyof T]-?: T[K] } // 删 ?
type Readonly<T> = { readonly [K in keyof T]: T[K] } // 加 readonly
type Mutable<T> = { -readonly [K in keyof T]: T[K] } // 删 readonly修饰符是保留的,不是覆盖的。
is(类型谓词)关键字让函数在返回 boolean 的同时收窄调用方。
ts
function isStr(x: unknown): x is string {
return typeof x === 'string'
}
declare const v: unknown
if (isStr(v)) {
v.toUpperCase()
}更强版本是 asserts x is T(断言函数),收窄整个后续作用域。
模板字面量类型TS 4.1ts
type World = `hello ${'world' | 'ts'}`
type Split<S extends string> = S extends `${infer A}-${infer B}` ? [A, B] : [S]
type R = Split<'a-b'>
${infer X} 是非贪婪匹配。
进阶
in / out 变型注解TS 4.7ts
interface Producer<out T> { get(): T } // 只出现在输出位置
interface Consumer<in T> { put(x: T): void } // 只出现在输入位置标反会报错;标注正确能让 TS 跳过结构化比较,检查更快。
const 类型参数TS 5.0ts
declare function f1<T>(x: T[]): T[]
declare function f2<const T>(x: T[]): T[]
const a = f1(['x', 'y'])
type A = typeof a
const b = f2(['x', 'y'])
type B = typeof b
NoInfer<T>TS 5.4阻止类型参数参与推断,只用于校验。
ts
declare function f<T>(items: T[], init: NoInfer<T>): T
// init 不会把 T 推宽,只做校验unique symbol关键字配合交叉类型做 branded type。
ts
declare const brand: unique symbol
type UserId = string & { readonly [brand]: 'UserId' }
type PostId = string & { readonly [brand]: 'PostId' }
declare const uid: UserId
declare const pid: PostId
// uid = pid 会报错