Typescript 泛型:给定键 K 和对象 T,约束 T[K] 的类型

Typescript generics: given key K and object T, constrain the type of T[K]

我的环境中,某些具有 id 属性 的对象每 "tick" 过期一次,需要使用 getObjectById 重新调用。我想实现一个 setter 方法来通过映射 thing.property => getObjectById(thing.property.id) 刷新某物的 属性。理想情况下,我希望此方法采用一个东西 T 和一个键 K,其中 T[K] 可以是一个具有 id (HasID) 或它们的数组的对象(HasID[]).

我相信我已经接近解决方案,但还不太正确。下面列出了我目前拥有的(class 上的静态方法,称为 $):

static refresh<T extends {[K in keyof T]: HasID}, K extends keyof T>(thing: T, key: K): void;
static refresh<T extends {[K in keyof T]: HasID[]}, K extends keyof T>(thing: T, key: K): void;
static refresh<T extends {[K in keyof T]: HasID | HasID[]}, K extends keyof T>(thing: T, key: K): void {
    if (_.isArray(thing[key])) {
        thing[key] = _.map(thing[key] as HasID[], s => getObjectById(s.id)) as HasID[];
    } else {
        thing[key] = getObjectById(thing[key].id) as HasID;
    }
}

例如,foo: {bar: HasID, baz: HasID[], biz: string[]} 所需的行为是:

$.refresh(foo, 'bar') // foo.bar = getObjectById(foo.bar.id)
$.refresh(foo, 'baz') // foo.baz = _.map(foo.baz, x=>getObjectById(x.id))
$.refresh(foo, 'biz') // error: foo.biz is not HasID or HasID[]
$.refresh(foo, 'boo') // error: 'boo' is not a key of foo

有人可以指出正确的方向来正确约束 T[K] 的类型吗?

你很接近,问题是不是 T 的每个键都必须是 HasIDHasID[],只有 K [=18] 指定的那个=]

class $ {
  static refresh<T extends { [P in K]: HasID }, K extends string>(thing: T, key: K): void;
  static refresh<T extends { [P in K]: HasID[] }, K extends string>(thing: T, key: K): void;
  static refresh<T extends { [P in K]: HasID | HasID[] }, K extends string>(thing: T, key: K): void {

  }
}

let foo: { bar: HasID, baz: HasID[], biz: string[] };
$.refresh(foo, 'bar') // foo.bar = getObjectById(foo.bar.id)
$.refresh(foo, 'baz') // foo.baz = _.map(foo.baz, x=>getObjectById(x.id))
$.refresh(foo, 'biz') // error: foo.biz is not HasID or HasID[]
$.refresh(foo, 'boo') // error: 'boo' is not a key of foo

此外,您可以使用 Record<K, HasId> 而不是 { [P in K]: HasID },我保留了您的原始版本,以便更容易跟踪差异。使用记录,签名将是:

class $ {
  static refresh<T extends Record<K, HasID>, K extends string>(thing: T, key: K): void;
  static refresh<T extends Record<K, HasID>[], K extends string>(thing: T, key: K): void;
  static refresh<T extends Record<K, HasID | HasID[]>, K extends string>(thing: T, key: K): void {

  }
}