Обобщения типа Typescript: заданный ключ K и объект T, ограничивают тип T [K] - PullRequest
0 голосов
/ 28 августа 2018

У меня есть среда, в которой определенные объекты, обладающие свойством id, истекают каждый "тик" и должны быть вызваны с помощью getObjectById. Я хотел бы реализовать метод установки для обновления свойства чего-либо путем сопоставления thing.property => getObjectById(thing.property.id). В идеале я хотел бы, чтобы этот метод взял вещь T и ключ K, где T[K] может быть объектом с идентификатором (HasID) или массивом из них (HasID[]).

Я верю, что близок к решению, но оно пока не совсем правильно. То, что у меня сейчас есть, перечислено ниже (статический метод для класса с именем $):

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]?

1 Ответ

0 голосов
/ 28 августа 2018

Вы близки, проблема в том, что не каждый ключ T должен быть HasID или HasID[], только тот, который указан K

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

Также вместо { [P in K]: HasID } вы можете использовать Record<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 {

  }
}
...