在TypeScript中,当你有一个函数接收多种类型的参数时,并且需要根据参数的类型来执行不同的逻辑,你可以使用几种不同的策略来优雅地处理这种情况。以下是一些建议:
类型守卫 (Type Guards):
类型守卫是一种表达式,它在运行时评估,并且返回一个布尔值,该值可以用于缩小类型检查器的类型范围。
function isAudioEngineOpts(opts: any): opts is AudioEngineOpts {
return !(opts as AudioEngineOpts).url; // 假设AudioEngineOpts没有url属性
}
function isAudioEngineRemoteOpts(opts: any): opts is AudioEngineRemoteOpts {
return (opts as AudioEngineRemoteOpts).url !== undefined;
}
function playBGM_COCOS(opts: AudioEngineOpts | AudioEngineRemoteOpts) {
if (isAudioEngineOpts(opts)) {
// opts 现在是 AudioEngineOpts
} else if (isAudioEngineRemoteOpts(opts)) {
// opts 现在是 AudioEngineRemoteOpts
}
}
注意,在这个例子中,我们假设AudioEngineOpts
不包含url
属性,所以可以使用!(opts as AudioEngineOpts).url
来判断。如果AudioEngineOpts
有其他独特的属性或方法可以用来区分,你应该使用那些。
类型断言 (Type Assertions):
如果你确信你知道参数的类型,并且想要告诉TypeScript忽略类型检查,你可以使用类型断言。但是,类型断言应该谨慎使用,因为它们会绕过TypeScript的类型系统。
function playBGM_COCOS(opts: AudioEngineOpts | AudioEngineRemoteOpts) {
if ('url' in opts) {
// 使用类型断言,但确保你确实知道这是安全的
const remoteOpts = opts as AudioEngineRemoteOpts;
// ...
} else {
// 假设这里除了AudioEngineRemoteOpts就是AudioEngineOpts
const engineOpts = opts as AudioEngineOpts;
// ...
}
}
注意,这里使用'url' in opts
作为类型检查的基础,但是仍然使用了类型断言。
- 使用联合类型与类型谓词 (Type Predicates):
如果你想要在类型系统中更清晰地表达这种情况,你可以定义一个类型谓词函数,该函数返回一个联合类型的缩小子集。 - 重载 (Overloads):
如果函数的行为在每种类型上都非常不同,你也可以考虑使用函数重载。但是,请注意,重载主要是用于函数签名,而不是用于在函数体内部进行类型区分。 - 可选链 (Optional Chaining) 和空值合并 (Nullish Coalescing):
虽然它们不直接用于类型检查,但在处理可能为undefined
或null
的属性时,它们是很有用的工具。 - discriminated unions (带区分联合):
如果你可以修改接口定义,添加一个区分属性(通常是一个字面量类型或枚举)到每个接口中,TypeScript 就可以更容易地区分它们。
选择哪种方法取决于你的具体需求和偏好。在大多数情况下,类型守卫和类型断言是处理这种情况的有效方法。但是,如果可能的话,最好避免在函数体内部进行复杂的类型检查,并尽量通过设计来减少这种需要。
看一下这个官方建议的解决方案:
使用类型谓词