确定用于联合接口的接口

Determine the interface used for a union interface

假设我有以下内容:

interface StringOp {
    (a: string, b: string): string;
}
interface NumberOp {
    (a: number, b: number): number;
}
function doThing(callback: StringOp | NumberOp) {
    if (callback is StringOp) {
        callback("a", "b");
    } else {
        callback(1, 2);
    }
}

我怎样才能真正将 callback is StringOp 表达为类型检查?


我试图将上面的内容简化为 MWE,但下面更接近我的实际用例:

interface TickFunction<T> {
  (val: T): void;
}
interface IndexedTickFunction<T> {
  (val: T, index: number): void;
}
function forEachTick<T>(callback: TickFunction<T> | IndexedTickFunction<T>) {
  ...
}

如果可能,我想继续使用箭头符号文字调用 forEachTick

在TS中,接口只存在于开发过程中,因此无法在运行时检查接口类型并执行smth。所以你必须以某种方式在你的接口中包含一个 "indicator" 以便在你创建你的回调方法时为它设置一些值。该指标可用于在运行时检查回调类型。 TS 还提供了 User-Defined Type Guards 所以最终的解决方案是这样的:

interface StringOp {
    opType: 'StringOp',
    (a: string, b: string): string;
}
interface NumberOp {
    opType: 'NumberOp',
    (a: number, b: number): number;
}

function isStringOp(op: StringOp | NumberOp): op is StringOp {
    return op.opType === 'StringOp';
}

function doThing(callback: StringOp | NumberOp) {
    if (isStringOp(callback)) {
        callback("a", "b");
    } else {
        callback(1, 2);
    }
}

我根据更新的问题添加了另一个示例:

interface TickFunction<T> {
  (val: T): void;
}
interface IndexedTickFunction<T> {
  (val: T, index: number): void;
}

function isTickFn<T>(fn: TickFunction<T> | IndexedTickFunction<T>): fn is TickFunction<T> {
    // in your example the indicator might be the function length
    // because it indicates the number of arguments expected by the function
    return fn.length === 1;
}

// I guess you also have to pass arguments to this function in order to pass them to your callback methods
function forEachTick<T>(callback: TickFunction<T> | IndexedTickFunction<T>, value: T, index?: number) {
  if (isTickFn(callback)) {
    callback(value);
  } else {
    callback(value, index);
  }
}

for (let i=0; i<10; i++) {
  forEachTick<string>((v: string) => console.log(v), 'some text');
}
for (let i=0; i<10; i++) {
  forEachTick<boolean>((v: boolean, index: number) => console.log(v, index), true, i);
}