# 系统化学习 TS 类型系统
目的:快速、系统性的入门 TS 类型系统
# 前言
TS 是什么?
TS 是 JS 的超集,
TS = JS + 类型系统
为了描述如此复杂(由于 JS 语言的灵活性/复杂性)的类型信息,类型系统表现出非常明显的编程语言特性。
以学习编程语言的方式,来学习 TS 类型系统
# 关键字/符号
- 类型: boolean, number, string, null, undefined, unknow, any, never
- 运算符: &, |, ...
- 声明: type, interface, declare
- 其他: readonly, keyof, extends, infer, ?, -?
# 语法
声明、条件分支、函数、循环/递归 是绝大部分语言的基础语法。
# 声明类型
type N = 1 | 2 | 3
# 声明类型结构
interface Point {
x: number
y: number
}
type Point3 = Point & { z: number } // 计算确切的结果
type Intersect<P, Q> = Pick<P, keyof P & keyof Q> // 描述计算过程,类似“函数”
一般我用 interface 描述静态类型(数据)结构,用 type 描述类型之间的计算关系
# 条件分支
declare function distance<P extends Point>(p1: P, p2: P): number
distance(0, 1) // Error
declare function promisify<T>(p: T): T extends Promise<any> ? T : Promise<T>
# 递归
循环不是编程语言的必要语法,循环的应用场景可被递归替代
type GenArr<
N extends number,
Arr extends any[] = []
> = Arr["length"] extends N ? Arr : GenArr<N, [...Arr, 1]>;
type ThreeItemArr = GenArr<3>
N, Arr
3, []
3, [1]
3, [1, 1]
3, [1, 1, 1] // Arr["length"] => 3
# 标准库
标准库是以语言自身基础能力实现的通用工具函数
深入了解一门语言的最佳方式之一是阅读标准库源码 可以学习如何灵活应用该语言基础特性、推荐风格、常用工具函数等等
以下是高频使用的标准库工具函数,其实现涵盖大部分 TS 类型系统的特性。
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Partial<{ x: string }>
// { x?: string }
type Required<T> = {
[P in keyof T]-?: T[P];
};
// Required<{ x?: string }>
// { x: string }
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// Readonly<{ x: string }>
// { readonly x: string }
type Exclude<T, U> = T extends U ? never : T;
// Exclude<"a" | "b" | "c", "a">
// "b" | "c"
type Extract<T, U> = T extends U ? T : never;
// Extract<"a" | "b" | "c", "a">
// "a"
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
// Pick<{ x: string, y: number }, 'x'>
// { x: string }
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
// Omit<{ x: string, y: number }, 'x'>
// { y: number }
type Record<K extends keyof any, T> = {
[P in K]: T;
};
// Record<string, string>
// { [k: string]: string }
type NonNullable<T> = T extends null | undefined ? never : T;
// NonNullable<null | 1 | 2>
// 1 | 2
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
// Parameters<(a: string, b: number) => Promise<string>>
// [string, number]
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
// ReturnType<(a: string, b: number) => Promise<string>>
// Promise<string>
# 练习
# 柯里化函数类型描述
type Curr<Args, R> = Args extends [infer First, ... infer Rest]
? (arg: First) => Curr<Rest, R>
: R
declare function curry<Fn extends (...args: any[]) => any> (fn: Fn):
Fn extends (...args: infer Args) => infer R ? Curr<Args, R> : never
function add (a: number, b: number): number {
return a + b
}
const currAdd = curry(add)
currAdd(1)(2) // 3
柯里化的 js 实现、用法参考 lodash.curry (opens new window)
# 获取 readonly 字段
type Equal<First, Second> = (<T>() => T extends First ? true : false) extends
(<T>() => T extends Second ? true : false)
? true : false
type GetReadonlyKeys<T> = {
[P in keyof T]-?: Equal<
{ [O in P]: T[P] },
{ -readonly [O in P]: T[P] }
> extends true
? never
: P
}[keyof T]
GetReadonlyKeys<{ readonly x?: string, y: string, readonly z: number }>
// { readonly x: "x", y: never, readonly z: "z" }["x" | "y" | "z"]
// "x" | "z"
# 资源
utility-types (opens new window)
type-challenges (opens new window)