基于参数枚举返回泛型类型的构造函数:TS2322

时间:2021-05-01 00:51:06

标签: typescript generics

我正在编写类型检查构造函数。 我希望返回对象类型具有与参数值完全相同的字段值,但另一个字段取决于该值。

function testConstructor<T extends En>(
  val: T
): {
  field: T;
  [x: string]: any;
} {
  switch (val) {
    case En.one:
      return {
        field: En.one,
        anotherField: 1,
      };
    case En.two:
      return {
        field: En.two,
        someAnotherField: 2,
      };
    default:
      return {
        field: val,
      };
  }
}

我收到一个错误:

TS2322: Type 'En.two' is not assignable to type 'T'. 'En.two' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'En'.

为什么我总是收到这个错误,我该如何解决? 为什么开关防护不起作用?


@Phu-Ngo 解决方案后的错误(此版本的代码更接近我的实际代码。)

enum Enum {
  one = 'one',
  two = 'two',
}

type Type1 = {
  field: typeof Enum.one;
  anotherField: number;
};

type Type2 = {
  field: typeof Enum.two;
  someAnotherField: number;
};

function testConstructor<T extends Enum>(
  val: T
): {
  field: T;
  [x: string]: any;
} {
  switch (val) {
    case Enum.one:
      let obj1: Type1 = {
        field: val,
        anotherField: 1,
      };
      return obj1;
    case Enum.two:
      let obj2: Type2 = {
        field: val,
        someAnotherField: 1,
      }
      return obj2;
  }
  throw Error(`unknown value ${val}`);
}

错误:

  1. 在第 field: val 行:
TS2322: Type 'T' is not assignable to type 'Enum.one'.   Type 'Enum' is not assignable to type 'Enum.one'.
  1. 在第 return obj1 行:
TS2322: Type 'Type1' is not assignable to type '{ [x: string]: any; field: T; }'.   Types of property 'field' are incompatible.     Type 'Enum.one' is not assignable to type 'T'.       'Enum.one' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Enum'.

1 个答案:

答案 0 :(得分:0)

不是用一个构造函数而是用 mapped 构造函数和 Extract 解决的。

enum Enum {
  one = 'one',
  two = 'two',
}

type Type1 = {
  field: typeof Enum.one;
  anotherField: number;
};

type Type2 = {
  field: typeof Enum.two;
  someAnotherField: number;
};

type TypeN = Type1 | Type2;

type ConstructorN<T> = () => Extract<
  TypeN,
  {
    field: T;
  }
>;

type Constructors = {
  [T in Enum]: ConstructorN<T>;
};

let constructors: Constructors = {
  [Enum.one]: () => {
    let obj: Type1 = {
      field: Enum.one,
      anotherField: 1,
    };
    return obj;
  },
  [Enum.two]: () => {
    let obj: Type2 = {
      field: Enum.two,
      someAnotherField: 2,
    };
    return obj;
  },
};

console.log(constructors[Enum.one]())
console.log(constructors[Enum.two]())