【问题标题】:Typescript autocompletion for conditionally returned function based on parameter of first function基于第一个函数参数的有条件返回函数的打字稿自动完成
【发布时间】:2023-01-24 19:06:49
【问题描述】:
我已经为此苦思冥想了一段时间,所以我决定在 Stackoverflow 上提出这个问题,希望有人能够帮助我。这是我的问题的简化代码 sn-p 版本:TypeScript Playground
解释:
一旦使用某个option 参数执行,我希望在example 函数上自动完成。
所以如果我输入example("foo").<autocompletion expects fooFunctions interface>。所以它会告诉我 example("foo").fooFunction() 是唯一的选择,因为第一个函数的参数是“foo”。
如果我输入example("bar").<autocompletion expects barFunctions interface>。所以它会告诉我 example("bar").barFunction() 是唯一的选择,因为第一个函数的参数是“bar”。
然而现在的问题是两个返回对象都期望另一个函数在那里,即使我不希望那样......
有没有 Typescript 专家可以帮助我?
【问题讨论】:
标签:
typescript
typescript-generics
【解决方案1】:
您可以使用重载来实现您的目标:
function example(options: 'foo'): fooFunctions;
function example(options: 'bar'): barFunctions;
function example(options: 'foo' | 'bar') : fooFunctions | barFunctions {
// The code
}
example('foo').fooFunction(); // Autocompletion works as expected
example('bar').barFunction(); // Autocompletion works as expected
exmaple('other'); // Fails because it is not an accepted
或者,您可以使用 keyof 来维护类似于您的语法:
interface returnFunctions {
bar: barFunctions,
foo: fooFunctions
}
const example = <T extends keyof returnFunctions>(options: T): returnFunctions[T] => {
// The code
}
这也使自动补全功能如您所愿地工作。
【解决方案2】:
您需要实际使用类型参数作为options 的类型:
const example = <T extends options>(options: T): returnFunctions[T] => {
///...
}
example("bar").barFunction()
example("foo").fooFunction()
Playground Link
现在使用这个版本,你在实现中仍然有错误,因为 TypeScript 不能真正遵循如果你缩小 option 返回的类型也会缩小。
您可以使用类型断言解决此问题:
const example = <T extends options>(options: T): returnFunctions[T] => {
if (options === "bar") {
return {
barFunction() {
console.log("It's the bar function");
}
} as returnFunctions[T]
}
if (options === "foo") {
return {
fooFunction() {
console.log("It's the foo function");
}
} as returnFunctions[T]
}
throw new Error(`Expected either foo or bar but got ${options}`)
}
Playground Link
或者您可以改用重载,公共签名是通用的,而实现签名不是通用的。虽然这不会增加安全性(您有责任确保参数和返回类型一致,但它确实使代码看起来更好 IMO)
function example<T extends Options>(options: T): ReturnFunctions[T]
function example(options: Options): ReturnFunctions[Options] {
if (options === "bar") {
return {
barFunction() {
console.log("It's the bar function");
}
}
}
if (options === "foo") {
return {
fooFunction() {
console.log("It's the foo function");
}
}
}
throw new Error(`Expected either foo or bar but got ${options}`)
}
Playground Link
此外,我将从 ReturnFunctions 接口派生选项,并使用大写字母作为类型(但那些只是吹毛求疵)Playground Link
【解决方案3】:
你可以在这里使用策略模式:
type options = "bar" | "foo";
interface barFunctions {
barFunction: () => void;
}
interface fooFunctions {
fooFunction: () => void;
}
interface returnFunctions {
bar: barFunctions,
foo: fooFunctions
}
const fnStrategy: returnFunctions = {
bar: {
barFunction() {
console.log("It's the bar function");
}
},
foo: {
fooFunction() {
console.log("It's the bar function");
}
}
}
const example = <T extends options>(options: T): returnFunctions[T] => fnStrategy[options]
const result = example('foo') // fooFUnctions