首页 > 解决方案 > 确定联合接口使用的接口

问题描述

假设我有以下内容:

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使用箭头符号文字进行调用:

标签: typescriptinterfacearrow-functionsgeneric-type-argumentunion-types

解决方案


在 TS 中,接口仅在开发过程中存在,因此无法在运行时检查接口类型并进行操作。因此,您必须以某种方式在您的界面中包含一个“指示器”,以便在您创建回调方法时为其设置一些值。该指标可用于在运行时检查回调类型。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);
}

推荐阅读