问题
I want to replicate lodash's _.omit function in plain typescript. omit should return an object with certain properties removed specified via parameters after the object parameter which comes first.
Here is my best attempt:
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;
}
Which gives me this error:
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
My interpretation of the error is that:
Since key is a
keyof TandTis an object, key can be asymbol,numberorstring.Since I use the
for inloop, key can only be astringbutincludesmight take anumberif I pass in an array, for example? I think. So that means there's a type error here?
Any insights as to why this doesn't work and how to make it work are appreciated!
回答1:
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;
}
For convenience I've pulled most of the typings to an interface.
The problem was that K has been being (correct tense?) inferred as a tuple, not as a union of keys. Hence, I changed it's type constraint accordingly:
[...(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
Then, we need to transform the tuple K to a union (in order to Exclude it from keyof T). It is done with K[number], which is I guess is self-explaining, it's the same as T[keyof T] creating a union of values of T.
Playground
回答2:
If we limit the type of keys to string [],It works. But it does not seem to be a good idea.Keys should be string | number | symbol[];
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;
// }
来源:https://stackoverflow.com/questions/53966509/typescript-type-safe-omit-function