0

我什至找不到合适的词来提出这个问题。这是我想要实现的简化代码。

class Test<T, TId extends keyof T> {
  public create(id: T[TId]): T {
    return {
      [TId]: id, // Error here. I want to set property `TId` to value `id`,
    } as T;
  }
}

interface A {
  id: number;
}

interface B {
  fileName: string;
}

new Test<A, 'id'>().create(123); // Want to get { id: 123 }
new Test<B, 'fileName'>().create('file'); // Want to get { fileName: 'file' }

错误是:Conversion of type '{ [x: number]: T[TId]; }' to type 'T' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. 'T' could be instantiated with an arbitrary type which could be unrelated to '{ [x: number]: T[TId]; }'.ts(2352)

4

1 回答 1

0

恐怕你试图做的事情不可能纯粹是类型。问题在这里:

new Test<A, 'id'>().create(123); // Want to get { id: 123 }

编译成这个 JavaScript:

new Test().create(123);

你可以看到这里不可能用正确的键返回一个对象,因为你的类型参数('id')在编译的代码中不存在。运行时不存在类型信息。

要解决此问题,您需要更改该设计,并将'id'作为字符串参数传递给createor 或Test()。例如:

class Test<T extends object, TId extends keyof T = keyof T> {

  constructor(private readonly key: TId) { }

  public create(value: T[TId]): T {
    return { [this.key]: value } as T;
  }
}

interface A {
  id: number;
}

interface B {
  fileName: string;
}

new Test<A>('id').create(123); // Returns { id: 123 }
new Test<B>('fileName').create('file'); // Returns { fileName: 'file' }

它通过自动推断进行编译TId,执行您想要的操作,并强制执行正确的键名和值(因此filePath作为键或传递数字fileName不会编译)。

于 2020-07-04T16:53:27.630 回答