如何编写抽象打字稿泛型?

时间:2020-05-27 07:55:53

标签: typescript generics typescript-generics

提供以下类型

interface Base {
  id: string;
}

interface A extends Base {
  propA: string;
}

interface B extends Base {
 propB: string;
}

我想表达一个具有以下约束的通用MyGeneric<T>

  1. T必须是一个对象
  2. T键必须为string
  3. T的值必须为instanceOf Base(类型为Base或扩展为Base的类型)

    (3。 T的值必须与Base接口兼容,但已对其进行了重新措辞,以免造成不理解)

我尝试过

interface MyConstraint {
  [key: string]: Base
}

interface MyGeneric<T extends MyConstraint> {
  data: T
}

但是在这种情况下,当用户想要使用它时,它有两个缺点:

interface userDefinedInterface1 {
  a: A;
  b: B;
}

function foo1(p: MyGeneric<userDefinedInterface1>):void {
  //drawback 1: this throws TS2344:
  //Type 'userDefinedInterface1' does not satisfy the constraint 'userDefinedInterface1'.   //Index signature is missing in type 'userDefinedInterface1'.
}

//to solve drawback 1, the user has to extend MyConstraint
interface userDefinedInterface2 extends MyConstraint {
  a: A;
  b: B;
}

function foo2(p: MyGeneric<userDefinedInterface2>):void {
  //drawback 2: here, ts considers every string property as valid because of [key: string] 
  console.log(p.data.arbitraryKey);//this is valid
}

有没有一种方法可以定义interface MyGeneric<T>来遵守上述3个约束,而又没有这2个缺点?

2 个答案:

答案 0 :(得分:0)

只有一个限制-如果要添加额外的密钥,则必须指定它们。

interface Base {
  id: string;
  num: number;
}

interface A extends Base {
  propA: string;
}

interface B extends Base {
 propB: string;
}

type MyGeneric<T extends Base, K extends keyof any = never> = {
  data: T & {
    [key in K | keyof T]: key extends keyof T ? T[key] : string;
  }
}


const test1: MyGeneric<B, 'test'> = {
  data: {
    id: '123',
    num: 123,
    propB: '123',
    test: '123',
  },
};
const test2: MyGeneric<B> = {
  data: {
    id: '123',
    num: 123,
    propB: '123',
    test: '123', // fails, it has to be provided in current TS.
  },
};

Playground

如果您只想依靠T的键。

然后只需使用此版本:

type MyGeneric<T extends Base> = {
  data: T & {
    [key in keyof T]: key extends keyof Base ? Base[key] : string;
  }
}

答案 1 :(得分:0)

我认为这应该解决您的两个缺点:

type MyConstraint<T> = {
  [K in keyof T]: T[K] extends Base ? T[K] : never;
};

interface MyGeneric<T extends MyConstraint<T>> {
  data: T;
}

以便宜的价格制作MyConstraint泛型。如果您做了类似的事情,那么您的两个示例现在都应该可以正常工作了,

interface UserDefinedInterface3 {
  a: A;
  b: B;
  c: string;
}

type Wrong = MyGeneric<UserDefinedInterface3>;

您会收到一条错误消息,指出属性c的类型不兼容。