修复智能感知以在函数重载中使用的 sub 属性 上提供正确的完成列表

Fix intellisense to provide correct completion list on sub property used within function overloads

我正在尝试为通用函数创建重载,其中函数的 return 类型由作为函数“道具”提供的对象上的 属性 的值确定.该函数具有必需的泛型类型,该类型也用于 return 类型,但它对 return 编辑的 return 类型没有影响。

我无法添加另一个泛型类型,因为我要么必须手动定义它,要么如果我设置默认类型并将其用在第一个参数上并由给定参数推断,它实际上不会从它只会使用默认类型 - 非常烦人。

我已经接近解决它了,但我剩下的唯一问题是 属性 值的智能感知没有显示 属性 的所有可用值,我是希望对 TypeScript 有更多了解的人可以帮助我克服最后的障碍。

我有一个 link to the typescript playground code here 以便您可以以最简单的形式修改它。

下面也是代码:

const EventType = Symbol('eventType');

type Unsubscribe = () => void;
type AnyFunction<ReturnType = any> = (...args: any[]) => ReturnType;

interface SingleResultEventCreateProps {
  mode: 'passthrough';
}

interface ArrayResultEventCreateProps {
  mode?: 'concurrent' | 'in-turn';
}

type EventCreateProps = SingleResultEventCreateProps | ArrayResultEventCreateProps

type ArrayResultEventDelegate<FuncType extends AnyFunction = AnyFunction> = ((delegate: FuncType) => Unsubscribe) & { [EventType]: 'array'; };
type SingleResultEventDelegate<FuncType extends AnyFunction = AnyFunction> = ((delegate: FuncType) => Unsubscribe) & { [EventType]: 'single'; };
type EventDelegate<FuncType extends AnyFunction = AnyFunction> = ArrayResultEventDelegate<FuncType> | SingleResultEventDelegate<FuncType>;

function create<FuncType extends AnyFunction>(): ArrayResultEventDelegate<FuncType>;
function create<FuncType extends AnyFunction>(props: ArrayResultEventCreateProps): ArrayResultEventDelegate<FuncType>;
function create<FuncType extends AnyFunction>(props: SingleResultEventCreateProps): SingleResultEventDelegate<FuncType>;
function create<FuncType extends AnyFunction>(props: EventCreateProps = {}): EventDelegate<FuncType> {
  return null as unknown as EventDelegate<FuncType>;
}

// the below works perfectly...except that the intellisense for mode offers only concurrent or in-turn, it does not offer passthrough as an possible value of mode.
const a = create<()=>string>({ mode: 'passthrough' }); // typeof a === SingleResultEventDelegate <-- correct
const b = create<()=>string>({ mode: 'concurrent' }); // typeof b === ArrayResultEventDelegate <-- correct
const c = create<()=>string>({ mode: 'in-turn' }); // typeof c === ArrayResultEventDelegate <-- correct

/*
to replicate my issue, remove the passthrough text from the mode property above, so it looks like this:

const a = create<()=>string>({ mode: '' });

then make sure your cursor is between the two quotes above and press ctrl + space and you'll see it only offers concurrent and in-turn as options.

Is there any way to offer all three modes via intellisense and still get the right return types?
*/

您似乎 运行 发现了 TypeScript 的一项缺失功能,已在 microsoft/TypeScript#44183 上报告。 TypeScript 似乎只选择其中一个重载来显示潜在的完成。您可以解决该问题,但不清楚何时或什至是否会解决此问题。


一种可能的处理方法是为您提供一个虚拟重载,它具有 所有 您想要支持的完成,但实际上永远不会是 select调用函数时编辑。有点像“documentation-only”超载。它可能看起来像这样:

// call signature 1
function create<F extends AnyFunction>(
): ArrayResultEventDelegate<F>;

// dummy overload for intellisense only
function create<F extends AnyFunction>(
  props: (ArrayResultEventCreateProps | SingleResultEventCreateProps) & { ugh: never }
): ArrayResultEventDelegate<F> | SingleResultEventDelegate<F>;

// call signature 2
function create<F extends AnyFunction>(
  props: ArrayResultEventCreateProps
): ArrayResultEventDelegate<F>;

// call signature 3
function create<F extends AnyFunction>(
  props: SingleResultEventCreateProps
): SingleResultEventDelegate<F>;

所以我在您遇到问题的两个之前添加了呼叫签名。它接受带有 {ugh: never}union of both the input props types, so it will suggest both. But it also is intersected,因此输入需要具有 never 类型的 ugh 属性。这是极不可能发生的,所以当你实际调用 create() 时,它不应该 select 这种组合重载。

让我们确保它有效。这些东西仍然表现得如你所愿:

const a = create<() => string>({ mode: 'passthrough' }); 
 // typeof a === SingleResultEventDelegate <-- correct
const b = create<() => string>({ mode: 'concurrent' }); 
// typeof b === ArrayResultEventDelegate <-- correct
const c = create<() => string>({ mode: 'in-turn' }); 
// typeof c === ArrayResultEventDelegate <-- correct

所以没有选择虚拟重载。但是该重载现在向您显示了可能的字符串完成的预期列表:

const d = create<() => string>({mode: ""})
//                                     ^  "concurrent"
//                                        "in-turn"
//                                        "passthrough"

Playground link to code