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

一曲冷凌霜 提交于 2019-11-30 09:49:53

问题


I'm playing with some code, which resolve generic type of property value and doesn't allow provide wrong value. But when I change from TValue to (t: TValue) => TValue, type TValue doesn't going resolved more. It's now unknown {} type, not number more

Example without function. Working good

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;
}

Example with function. Don't working as expected

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;
}

I'm expected TValue equal number in example with function as in example without function


回答1:


This is a known issue, as you are aware from your GitHub comments. Summarizing here:

Currently, type inference does not work the way you want it to, as the compiler treats the original as equivalent to something like this:

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

And the calls to decorate() in barDeco and wrongBarDeco don't have enough type information for the compiler to infer the generic type, and thus it is inferred as {}, resulting in much sadness. The decorator is basically a curried function f(x)(y), and to fix this the compiler would have to infer the type of f from the type of y, which is a new sort of contextual typing. Maybe decorators could be special-cased for such inference; it would likely be a massive breaking change to do that with curried functions in general.

For now the only way to deal with this is to manually specify the generic parameter when calling the decorator as in

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

or to manually annotate your callback as in

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

These workarounds are not optimal, but they do work, so you have some way to deal with things unless and until Microsoft/TypeScript#2607 is addressed. There are many, many open issues, so I wouldn't expect to see much movement on this one. The likelihood will increase if more people go to that issue and give it a 👍 and describe compelling use cases and compelling reasons why the workarounds are insufficient. Since you have already done this, I think there isn't much left for you to do but move on. If future readers care about this, they can check out the issue in GitHub and contribute.

Sorry there isn't a better answer for you. Good luck!



来源:https://stackoverflow.com/questions/55409151/how-to-resolve-generic-type-of-property-value-from-decorated-property-in-decorat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!