函数重载
函数重载可以描述复杂的函数类型
ts
function fn(num: number): number
function fn(str: string): string
function fn(arg: number | string) {
if (typeof arg === 'number') return 0
return '1'
}
不过这种方法适用范围有限 不能导出一个类型用于定义其他函数 而且需要把重载的类型在参数里额外写一次
函数的调用类型
也可以直接描述其调用类型
ts
type FN = {
(num: number): number
(str: string): string
}
但这个类型只能用于描述函数 不能用于定义函数 会让ts对类型产生误判
ts
const fn: FN = (arg) => {
if (typeof arg === 'number') return 0
return '1'
}

如果将回调函数声明为这个类型 写代码的时候会完全失去类型提示
TypeMap构造函数参数
ts
type ArgMap = {
number: { num: number }
string: { str: string }
}
type FN = (
config: {
[K in keyof ArgMap]: ArgMap[K] & {
type: K
}
}[keyof ArgMap],
) => void
const fn: FN = (config) => {
switch (config.type) {
case 'string':
console.log(config.str)
break
case 'number':
console.log(config.num)
}
}
可以利用类型索引自由构造不同类型的参数 这里构造为对象并用type区分并不强制 甚至可以构造args数组
类型非常完备 但无法约束返回值
TypeMap+泛型
typescript
type FN = <K extends keyof FnTypeMap>(type: K, value: FnTypeMap[K]['in']) => FnTypeMap[K]['out']
const fn: FN = (type, value) => {
switch (type) {
case 'number': {
value // FnTypeMap[K]["in"] 并没有收窄类型
break
}
case 'string': {
break
}
}
}
类型依旧不完备
结论
复杂函数类型在ts中没有一个完美的解决方案 定义函数时重载即可 需要以一个类型约束函数的时候(例如描述回调函数或者使用zustand时)使用TypeMap构造参数对象 可以用onComplete等替代返回值