首页 > 解决方案 > 用于函数参数的 TypeScript 接口和泛型

问题描述

我需要创建多个测试,并且我想确保设置是强类型的。我正在阅读泛型文档,我可以理解简单的案例,但我迷失了高级示例。

我尝试做一些类似于上一个例子中描述的事情。

有两个测试函数签名,根据测试值和条件值而变化:

interface ITestFuncWithCond<ValueType, ConditionType> {
   (value: ValueType, condition: ConditionType): boolean;
};

interface ITestFuncNoCond<ValueType> {
   (value: ValueType): boolean;
};

这个想法是对测试做这样的事情(注意类型):

// Example A
// No condition param
const testA: Tester = createTest(StringTestIsEmpty);
// Value param type must be string.
testA.test("myString");

// Example B
// condition param is number
const testB: Tester = createTest(StringLengthEquals, 2);
// Value param type must be string.
testB.test("myString");

// Example C
// condition param is string
const testC: Tester = createTest(StringEquals, "myString");
// Value param type must be string.
testC.test("myString");

// Example D
// condition param is number
const testD: Tester = createTest(NumberEquals, 10);
// Value param type must be number.
testD.test(5);

这是我的伪代码,但正如你可能看到的,我需要一些指导词来解开这个问题。

我更新了下面的代码,试图更好地解释我在找什么。 这是一个沙箱


// Test implementations
const _StringTestIsEmpty = (x: string): boolean => {
  console.log(x);
  return x === "";
}
const _StringLengthEquals = (x: string, l: number): boolean => {
  console.log(x, l);
  return x.length === l;
}
const _StringEquals = (x: string, y: string): boolean => {
  console.log(x, y);
  return x === y;
}
const _NumberEquals = (x: number, y: number): boolean => {
  console.log(x, y);
  return x === y;
}

// Different tests available
enum TestName {
  StringTestIsEmpty,
  StringLengthEquals,
  StringEquals,
  NumberEquals
}

// Test signatures.
interface ITestFuncWithCond<ValueType, ConditionType> {
  (value: ValueType, condition: ConditionType): boolean;
};

interface ITestFuncNoCond<ValueType> {
  (value: ValueType): boolean;
};

class Tester<ConditionType, ValueType> {
  name: TestName;
  condition?: ConditionType;
  // ValueType??? ConditionType ???
  private testFunction: ITestFuncWithCond<ValueType, ConditionType> | ITestFuncNoCond<ValueType>

  // Test constructor with different signatures.
  constructor(testName: TestName);
  constructor(testName: TestName, condition: ConditionType);
  constructor(testName: TestName, condition?: ConditionType) {
    this.name = testName;
    this.condition = condition;
    // Here, the testName is used to fetch the actual test function (testImpl)
    this.testFunction = this.getFunction(testName)

  }

  // ValueType should be string for test A, B and C, and should be number for test D.
  // "this.condition" should be undefined in test A.
  // "this.condition" should be "number for test B and D, and "string" for testC.
  test(value: ValueType): boolean {
    if (this.condition) {
      return this.testFunction(value, this.condition);
    } else {
      return this.testFunction(value);
    }
  }

  private fetchTestFunction<V, C extends [] | [condition: any]>(iTestFunc: (value: V, ...conditionMaybe: C) => boolean, ...conditionMaybe: C) {
    return {
      test: (v: V) => iTestFunc(v, ...conditionMaybe)
    }
  }

  private getFunction(testName: TestName) {
    switch (testName) {
      case TestName.StringTestIsEmpty: {
        return this.fetchTestFunction(_StringTestIsEmpty);
      }
      case TestName.StringLengthEquals: {
        return this.fetchTestFunction(_StringLengthEquals, this.condition);
      }
      case TestName.StringEquals: {
        return this.fetchTestFunction(_StringEquals, this.condition);
      }
      case TestName.NumberEquals: {
        return this.fetchTestFunction(_NumberEquals, this.condition);
      }
    }
  }
}

function createTest(testName: TestName, condition?: any): Tester {
  // set up to properly fetch the right test
  return new Tester(testName, condition);
}

// Example A
// No condition param
// don't annotate Tester because it must be generic
const testA = createTest(TestName.StringTestIsEmpty);
// Value param type must be string.
testA.test("myString");

// Example B
// condition param is number
const testB = createTest(TestName.StringLengthEquals, 2);
// Value param type must be string.
testB.test("myString");

// Example C
// condition param is string
const testC = createTest(TestName.StringEquals, "myString");
// Value param type must be string.
testC.test("myString");

// Example D
// condition param is number
const testD = createTest(TestName.NumberEquals, 10);
// Value param type must be number.
testD.test(5);

标签: typescripttypescript-generics

解决方案


推荐阅读