TypeScript 开发笔记
TypeScript 类型系统和最佳实践。
类型基础
基本类型
ts
// 基础类型
let str: string = 'hello'
let num: number = 123
let bool: boolean = true
let arr: number[] = [1, 2, 3]
let tuple: [string, number] = ['a', 1]
let obj: { name: string; age: number } = { name: 'saqqdy', age: 18 }
// 联合类型
let id: string | number
// 类型别名
type UserID = string | number
type User = {
id: UserID
name: string
email?: string // 可选属性
}泛型
ts
// 泛型函数
function identity<T>(arg: T): T {
return arg
}
identity<string>('hello')
identity(123) // 类型推断
// 泛型约束
interface Lengthwise {
length: number
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length)
return arg
}
// 多类型参数
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value]
}高级类型
工具类型
基于 js-cool 的类型设计:
ts
// Partial - 可选
type PartialUser = Partial<User>
// Required - 必选
type RequiredUser = Required<User>
// Pick - 选取
type UserName = Pick<User, 'name'>
// Omit - 排除
type UserWithoutEmail = Omit<User, 'email'>
// Record - 记录
type UserMap = Record<string, User>
// DeepPartial - 深层可选
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}条件类型
ts
// 条件类型
type IsString<T> = T extends string ? true : false
type A = IsString<string> // true
type B = IsString<number> // false
// infer 推断
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
function getName(): string { return 'saqqdy' }
type Name = ReturnType<typeof getName> // string模板字面量类型
ts
type EventName = 'click' | 'focus' | 'blur'
type Handler = `on${Capitalize<EventName>}`
// 'onClick' | 'onFocus' | 'onBlur'
type PropName = `data-${string}`
// `data-${string}`类型推断
类型守卫
ts
function isString(val: unknown): val is string {
return typeof val === 'string'
}
function process(val: string | number) {
if (isString(val)) {
// val 是 string
console.log(val.toUpperCase())
} else {
// val 是 number
console.log(val.toFixed(2))
}
}
// instanceof
class Dog { bark() {} }
class Cat { meow() {} }
function speak(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark()
} else {
animal.meow()
}
}类型断言
ts
// 尖括号
let value: any = 'hello'
let length: number = (<string>value).length
// as 语法 (推荐)
let length2: number = (value as string).length
// 非空断言
let element = document.getElementById('app')!
element.innerHTML = 'Hello'库开发
类型声明
ts
// global.d.ts
declare module 'my-lib' {
export function format(date: Date, pattern: string): string
export function clone<T>(obj: T): T
export const version: string
export interface Options {
timeout?: number
retry?: number
}
export default function(options: Options): void
}多格式输出
json
// package.json
{
"name": "my-lib",
"version": "1.0.0",
"type": "module",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.mjs",
"require": "./dist/index.cjs",
"types": "./dist/index.d.ts"
}
},
"files": ["dist"]
}类型导出
ts
// index.ts
export type { User, Options } from './types'
export { format, clone } from './utils'
export { default as MyLib } from './core'实用技巧
递归类型
ts
// 深度只读
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P]
}
// 深度必填
type DeepRequired<T> = {
[P in keyof T]-?: T[P] extends object
? DeepRequired<T[P]>
: T[P]
}类型组合
ts
// 合并类型
type Merge<A, B> = Omit<A, keyof B> & B
type Base = { id: string; name: string }
type Extra = { name: number; age: number }
type Merged = Merge<Base, Extra>
// { id: string; name: number; age: number }函数重载
ts
function parse(input: string): Date
function parse(input: Date): string
function parse(input: string | Date) {
if (typeof input === 'string') {
return new Date(input)
}
return input.toISOString()
}
const date = parse('2024-01-01') // Date
const str = parse(new Date()) // string相关项目
- js-cool - JS/TS 工具库
- node-kit - Node.js 工具集
- eslint-sets - ESLint 配置