Ввод обратного вызова, получающего пару ключ-значение объекта в typescript

#typescript #typescript-typings

#typescript #typescript-типизации

Вопрос:

Я написал функцию в typescript 4.0.5, которая проходит через объект и выполняет заданный обратный вызов для каждого свойства, аналогично array.forEach (см. Ниже). Если noImplicitAny: true задано в tsconfig, ввод функции, похоже, работает только в том случае, если все свойства имеют один и тот же тип.

 export function forEachKey<T extends Object>(obj: T, cb: (key: Extract<keyof T, string>, value: T[keyof T]) => void): void {
  for (const k in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, k)) {
      cb(k, obj[k]);
    }
  }
}
 

В следующем примере ошибок нет:

 interface TestNumber {
  a: number;
  b: number;
}
const t1Nr: TestNumber = { a: 1, b: 2 };
const t2Nr: TestNumber = { a: 3, b: 4 };
forEachKey(t1Nr, (key, value) => {
  t2Nr[key] = value;
});
 

Но как только появятся свойства с другим типом:

 interface TestMixed {
  a: number;
  b: string;
}
const t1: TestMixed = { a: 1, b: '2' };
const t2: TestMixed = { a: 1, b: '2' };
forEachKey(t1, (key, value) => {
  t2[key] = value;    // error
});
 

Я получаю сообщение об ошибке:

TS2322: Type 'string | number' is not assignable to type 'never'.   Type 'string' is not assignable to type 'never'.

Насколько я вижу, мне нужно объявить тип value as T[key] , но я не смог найти способ сделать это.

Кто-нибудь знает, как исправить ввод текста? Заранее спасибо


Редактировать:

После того, как ответ Алексея работал с этим простым примером, я попытался преобразовать пример в мою текущую реализацию. Там у нас есть класс, подобный следующему:

 
class TestMixed2Class implements TestMixed {
  constructor (public a: number = 1, public b: string = '2', public c: number = 3) {}

  setParams(params: TestMixed) {
    forEachKey(params, (key, value) => this[key] = value);   // error
  }
}
 

Это приводит к ошибке

 TS2322: Type 'TestMixed[K]' is not assignable to type 'this[K]'.
   Type 'TestMixed' is not assignable to type 'this'.
     'this' could be instantiated with an arbitrary type which could be unrelated to 'TestMixed'.
       Type 'TestMixed[K]' is not assignable to type 'never'.
         Type 'string | number' is not assignable to type 'never'.
           Type 'string' is not assignable to type 'never'.
 

Есть ли также способ добиться правильного ввода здесь?

Ответ №1:

Вы можете ввести параметр общего типа во внутренней функции for key , чтобы Typescript мог сопоставлять key и value :

 function forEachKey<T extends object>(obj: T, cb: <K extends keyof T>(key: K, value: T[K]) => void): void {
  for (const k in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, k)) {
      cb(k, obj[k]);
    }
  }
}
 

Игровая площадка

Комментарии:

1. Спасибо, это работает. К сожалению, у меня возникли еще некоторые проблемы при переводе примера в нашу реализацию (см. Редактирование)