キースクリプトでキー頭痛


“定期的な”JavaScriptを行うの長年の後、私は最近(最終的に)TypesScriptで私の足を濡らす機会があった.「私は5分でそれを拾います」と大胆に私に話している何人かの人々にもかかわらず.私はもっと分別がある.
ほとんどの場合、それは高速でピックアップしやすいです.しかし、新しいパラダイムへの転換は、常にエッジケースの周りにハングアップします.Typescriptはこれに例外ではありません.
私はすでに、私たちが反応/TSと反応しているのと同じようなコンベンションの下でデフォルトのプロップ値を定義するためにジャンプしなければならなかったハードルに関する2つの長いポストを書きました.私の最新の難問は、オブジェクトキーの取り扱いに関係しています.

問題


私はJavaScriptを使用しているとき、私は頻繁に様々なオブジェクトに対処する必要があります.あなたがどんなJS開発をしたならば、あなたは私が「オブジェクト」について話しているということを知っています.私が遭遇するように見えるJSオブジェクトの大多数はHashMapsと同等です.
例えば、以下のような2つのオブジェクトを持つことはとても一般的です.
const user1 = {
  name: 'Joe',
  city: 'New York',
  age: 40,
  isManagement: false,
};

const user2 = {
  name: 'Mary',
  city: 'New York',
  age: 35,
  isManagement: true,
};
あまり複雑ではない、右?これらの“オブジェクト”だけです.データ構造.
それで、私はしばしば、2人のユーザーが一般的に何を持っているかを見つける必要があると想像しましょう.私のアプリは頻繁にこの評価を必要とするので、私は任意の2つのオブジェクトを受け入れるユニバーサル機能を作成し、それらのオブジェクトが共通のキー値を教えてください.
JavaScriptでは、このような小さな功利的な機能を素早く取り消すことができました.
const getEquivalentKeys = (object1: {}, object2 = {}) => {
   let equivalentKeys = [];
   Object.keys(object1).forEach(key => {
      if (object1[key] === object2[key]) {
         equivalentKeys.push(key);
      }
   });
   return equivalentKeys;
}
[注] :私はこれをより効率的に行うことができることを認識します.map() 関数.しかし、私は、これが少しより明確であると思います(意味:より詳細な)この説明の目的のために
上記の関数を使用すると、次のようになります.
console.log(getEquivalentKeys(user1, user2));
// logs: ['city']
関数の結果はuser1 and user2 共有共通の都市.きれいなダンは、右?
では、これをタイプスクリプトに変換しましょう.
const getEquivalentKeys = (object1: object, object2: object): Array<string> => {
   let equivalentKeys = [] as Array<string>;
   Object.keys(object1).forEach((key: string) => {
      if (object1[key] === object2[key]) {
         equivalentKeys.push(key);
      }
   });
   return equivalentKeys;
}
これは私にとって右に見える.閉じるこの動画はお気に入りから削除されています.具体的には、この行は以下のようになります.
if (object1[key] === object2[key]) {
TS says :Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
うーん
明らかに、私は簡単に定義するインターフェイスを使用できることを知っているuser 型を入力し、関数シグネチャに宣言します.しかし、私はこの機能を任意のオブジェクトで動作します.そして、私はtsがなぜそれについて不満を言っているかについて理解します-しかし、私は確かにそれを好みません.TSは、どのようなタイプがジェネリックにインデックスを付けるかを知らないので、文句を言いますobject .

ジェネリックとの相撲


既にJava & C CHERUNI開発を行っていたので、これはジェネリックのユースケースだとすぐに思いました.そこで試してみました.
const getEquivalentKeys = <T1 extends object, T2 extends object>(object1: T1, object2: T2): Array<string> => {
   let equivalentKeys = [] as Array<string>;
   Object.keys(object1).forEach((key: string) => {
      if (object1[key] === object2[key]) {
         equivalentKeys.push(key);
      }
   });
   return equivalentKeys;
}
しかし、これは前の例と同じ問題につながります.TSはまだそのタイプを知らないstring はインデックス{} . そして、なぜそれが文句を言うのか理解しています.
const getEquivalentKeys = <T1 extends object, T2 extends object>(object1: T1, object2: T2): Array<string> => {
は以下の関数と等価である:
const getEquivalentKeys = (object1: object, object2: object): Array<string> => {
それで、私はいくつかのより明白な鋳造を試みました.
const getEquivalentKeys = <T1 extends object, T2 extends object>(object1: T1, object2: T2): Array<string> => {
   let equivalentKeys = [] as Array<string>;
   Object.keys(object1).forEach((key: string) => {
      const key1 = key as keyof T1;
      const key2 = key as keyof T2;
      if (object1[key1] === object2[key2]) {
         equivalentKeys.push(key);
      }
   });
   return equivalentKeys;
}
さて、TSは再びこの行に文句を言います.
if (object1[key1] === object2[key2]) {
今回はThis condition will always return 'false' since the types 'T1[keyof T1]' and 'T2[keyof T2]' have no overlap.これは私が自分のモニターで叫んでいるところです.

Yes, they do have an overlap!!!


悲しいことに、私のモニターは静寂で私を見つめています.
それが言われて、1つの迅速かつ汚れた方法は、この作品を作ることです.
const getEquivalentKeys = <T1 extends any, T2 extends any>(object1: T1, object2: T2): Array<string> => {
   let equivalentKeys = [] as Array<string>;
   Object.keys(object1).forEach((key: string) => {
      if (object1[key] === object2[key]) {
         equivalentKeys.push(key);
      }
   });
   return equivalentKeys;
}
Voila!TSには苦情がない.しかし、タイプスクリプトが文句を言わないかもしれないとしても、私は不平を言います-多く.鋳造してT1 and T2 ASany , それは基本的に我々がTSで得ることになっている素晴らしい魔法のどれでも破壊します.私がこれのような機能を作り出し始めるつもりであるならば、TSを使う際に、本当に意味がありませんgetEquivalentKeys() そして、TSは誰も賢くないでしょう.
描画ボードに戻る.

インタフェースとのレスリング


一般的に、オブジェクトの型についてのtsを明示的に伝えるときは、インターフェイスを使用します.そのためには、
interface GenericObject {
   [key: string]: any,
}

const getEquivalentKeys = (object1: GenericObject, object2: GenericObject): Array<string> => {
   let equivalentKeys = [] as Array<string>;
   Object.keys(object1).forEach((key: string) => {
      if (object1[key] === object2[key]) {
         equivalentKeys.push(key);
      }
   });
   return equivalentKeys;
}
そしてこれは動作します.として、それは私たちがそれを行うと期待して正確に行います.これは、オブジェクトだけが関数に渡されることを保証します.
しかし、私はここで正直でなければなりません-それは、本当に私のたわごとを悩まします.多分、数ヶ月で、私はもうこれについてあまり気にしないでしょう.しかし、今、いくつかの理由で、それは本当に私は、私はTSobject にインデックスを付けるstring .

コンパイラの説明


このシリーズの私の最初の記事では、ユーザーは素晴らしいコメント(強調:)をしました:

I'm a dyed in the wool C# programmer and would love to be pulling across the great parts of that to the JS world with TypeScript. But yeah, not if I'm going to spend hours of my life trying to explain to a compiler my perfectly logical structure.


よく、マイク.よく言った.

なぜこれは私を悩ますのですか?


TSについて学ぶ最初のことの一つは、JavaScriptのスーパーセットであるということです.今、私は、あなたが本当にTSの強みを活用したいならば、TSコンパイラが好きでないというLotta「ベース」JSコードがあるでしょう.
しかし、キーによってオブジェクトの値を参照する(タイプ:文字列キー)は、私は特別なものを作成する必要がありますと思うように困惑しているGenericObject コンパイラに説明するためのインターフェースです

Yeah... this object can be indexed by a string.


つまり、その作品.しかし、それが私がこれをすることになっているならば、それはちょうど私を考えさせます:

Wait... what???


それは、私があなたに私に言わなければならない同じちょっと不快感ですstring 文字と数字と特殊文字を含めることができます.
私がそれを回避する方法を考え出した今、私は、それがちょうどあなたが「慣れている」ものの1つであると思います.または多分、私がts(コアの強さを無効にすることなく)を回避するのを許すtsの若干の単純なテクニックがあるかもしれません.しかし、その不思議な解決が存在するならば、私のパレットのgoogling技術はまだそれを明らかにしていません.