如何从装饰器中装饰的 属性 解析 属性 值的通用类型

How to resolve generic type of property value from decorated property in decorator

我正在玩一些代码,它解析 属性 值的通用类型并且不允许提供错误的值。但是当我从 TValue 更改为 (t: TValue) => TValue 时,键入 TValue 并没有解决更多问题。现在未知 {} 类型,而不是 number 更多

没有功能的例子。工作顺利

type ProtoOf<T> = Pick<T, keyof T>;

function decorate<TValue>(value: TValue) {
  return <T extends { [KA in TKey]: TValue }, TKey extends keyof T>(
    proto: ProtoOf<T> & { [P in TKey]: TValue },
    propertyKey: TKey
  ) => {};
}

class Foo {
  // TS error: none
  // Result: EXPECTED
  @decorate(1) bar: number = 1;

  // TS Error:
  // Types of property 'wrongBar' are incompatible
  // Type 'number' is not assignable to type 'string'
  // Result: EXPECTED
  @decorate('') wrongBar: number = 1;
}

具有功能的示例。没有按预期工作

type ProtoOf<T> = Pick<T, keyof T>;

function decorate<TValue>(getValue: (t: TValue) => TValue) {
  return <T extends { [KA in TKey]: TValue }, TKey extends keyof T>(
    proto: ProtoOf<T> & { [P in TKey]: TValue },
    propertyKey: TKey
  ) => {};
}

class Foo {
  // TS Error: Operator '+' cannot be applied to types '{}' and '1'
  // Result: NOT EXPECTED: because we can assign `number` to `number`
  @decorate(v => v + 1) bar: number = 1;

  // TS error: none
  // Result: NOT EXPECTED: we should have error, we cannot assign `string` to `number`
  @decorate(v => v + '') wrongBar: number = 1;
}

我希望 TValue 等于 number 在有功能的例子中和在没有功能的例子中一样

这是一个 known issue,正如您从 GitHub 评论中所知道的那样。总结在这里:

目前,类型推断无法按照您希望的方式工作,因为编译器将原始类型视为等同于以下内容:

const barDeco = decorate(v => v + 1); // error
barDeco(Foo.prototype, "bar");
const wrongBarDeco = decorate(v => v + '');
wrongBarDeco(Foo.prototype, "wrongBar");

并且 barDecowrongBarDeco 中对 decorate() 的调用没有足够的类型信息供编译器推断泛型类型,因此被推断为 {},导致很多悲伤。装饰器基本上是一个咖喱函数 f(x)(y),要解决这个问题,编译器必须从 y 的类型推断出 f 的类型,这是一种新的上下文类型。也许装饰器可以作为这种推断的特例;一般来说,用柯里化函数来做这可能是一个巨大的突破性变化。

目前处理这个问题的唯一方法是在调用装饰器时手动指定泛型参数,如

class Foo {
  @decorate<number>(v => v + 1) bar: number = 1; // okay
  @decorate<number>(v => v + '') wrongBar: number = 1; // error
}

或像

中那样手动注释您的回调
class Foo {
  @decorate((v: number) => v + 1) bar: number = 1; // okay
  @decorate((v: number) => v + '') wrongBar: number = 1; // error
}

这些变通办法不是最优的,但它们确实有效,因此您有一些方法来处理事情,除非并直到 Microsoft/TypeScript#2607 得到解决。有很多很多未解决的问题,所以我不希望在这个问题上看到太多进展。如果有更多人关注该问题并给出解决方案并描述令人信服的用例和令人信服的解决方法不足的原因,则可能性会增加。既然你已经这样做了,我认为除了继续前进,你没有什么可做的了。如果未来的读者关心这个,他们可以在 GitHub 中查看问题并做出贡献。

抱歉,没有更好的答案。祝你好运!