web-dev-qa-db-ja.com

TypeScriptタイプセーフ除外関数

Lodashの_.omit関数を単純なTypeScriptで複製したい。 omitは、最初に来るオブジェクトパラメータの後に、パラメータを介して指定された特定のプロパティが削除されたオブジェクトを返す必要があります。

これが私の最善の試みです:

function omit<T extends object, K extends keyof T>(obj: T, ...keys: K[]): {[k in Exclude<keyof T, K>]: T[k]} {
    let ret: any = {};
    let key: keyof T;
    for (key in obj) {
        if (!(keys.includes(key))) {
            ret[key] = obj[key];
        }
    }
    return ret;
}

これは私にこのエラーを与えます:

Argument of type 'keyof T' is not assignable to parameter of type 'K'.
  Type 'string | number | symbol' is not assignable to type 'K'.
    Type 'string' is not assignable to type 'K'.ts(2345)
let key: keyof T

エラーの私の解釈はそれです:

  1. Keyはkeyof Tであり、Tはオブジェクトであるため、keyはsymbolnumberまたはstringにすることができます。

  2. 私はfor inループを使用しているため、keyはstringのみにすることができますが、たとえば、配列で渡す場合、includesnumberをとる可能性があります。おもう。つまり、ここに型エラーがあるということですか?

これが機能しない理由とそれを機能させる方法についての洞察は高く評価されています!

8
Salami
interface Omit {
    <T extends object, K extends [...(keyof T)[]]>
    (obj: T, ...keys: K): {
        [K2 in Exclude<keyof T, K[number]>]: T[K2]
    }
}

const omit: Omit = (obj, ...keys) => {
    let ret = {} as {
        [K in keyof typeof obj]: (typeof obj)[K]
    };
    let key: keyof typeof obj;
    for (key in obj) {
        if (!(keys.includes(key))) {
            ret[key] = obj[key];
        }
    }
    return ret;
}

便宜上、入力のほとんどをインターフェイスにプルしました。

問題は、KnionではなくTupleとして推論されている(正しい時制?)ことでした。したがって、それに応じてタイプ制約を変更しました。

[...(keyof T)[]] // which can be broke down to:
keyof T // a union of keys of T
(keyof T)[] // an array containing keys of T
[] // a Tuple
[...X] // a Tuple that contains an array X

次に、タプルKを共用体に変換する必要があります(keyof TからExcludeにするため)。これはK[number]で行われます。これは自明のとおりです。T[keyof T]と同じで、Tの値の結合を作成します。

遊び場

5

キーのタイプを文字列[]に制限すると、機能します。しかし、それは良い考えではないようです。番号|シンボル[];

function omit<T, K extends string>(
  obj: T,
  ...keys: K[]
): { [k in Exclude<keyof T, K>]: T[k] } {
  let ret: any = {};
  Object.keys(obj)
    .filter((key: K) => !keys.includes(key))
    .forEach(key => {
      ret[key] = obj[key];
    });
  return ret;
}
const result = omit({ a: 1, b: 2, c: 3 }, 'a', 'c');
// The compiler inferred result as 
// {
//   b: number;
// }
0
郭一凡