0

I'd like to write this function in typescript:

const pick = (obj, keys) => {
  if (!Array.isArray(keys)) keys = keys.split(',')
  return keys.reduce((acum, key) => (acum[key] = obj[key], acum), {})
}

const o = {
  a: 1,
  b: 2,
  c: 3
}

console.log('o[a,c]:', pick(o, 'a,c'))        // { a: 1, c: 3 }
console.log('o[a,c]:', pick(o, ['a', 'c']))   // { a: 1, c: 3 }

I already saw this answer which seems like a good starting point but I can't figure out how to convert the string to a K[].

Or can I just somehow tell Typescript to trust me, and refrain from checking types?

1
  • 1
    Have you tried casting? keys =<K[]> keys.split(',') Commented Jun 14, 2019 at 6:25

2 Answers 2

3

Typescript compiler doesn't know what your string will be when you're evaluating it with the split, therefore you must force K[] on it and that will return all the properties of T.

Based on your desired usage, only the second one is viable for getting the desired types.

// i changed the "a" property to a string
const o = { a: 'hello', b: 2, c: 3 };

// <T, K extends keyof T> - here you assign the generics
// T - will be used on "obj" parameter so it can inherit it's properties
// K - will be a property of T
// I typed the "keys" parameter with "string" (to respect your first usage) and K[] (an array of K properties , for the second one)
// At last, we want the function to return K props of T, we have the Pick construct for that.
const pick = <T, K extends keyof T>(obj: T, keys: string | K[]): Pick<T, K> => {
    if (!Array.isArray(keys)) keys = (keys as string).split(',') as K[]; // we know that "keys" is a string, so we'll force the type on it, and we'll force K[] on the .split result, this will return all types from T.
    return keys.reduce((acum, key: K) => (acum[key] = obj[key], acum), {} as T ); // here we mark the accumulator as T, so we know what properties are used.
};

let p1 = pick(o, 'a,c'); // { a: string , b: number, c: number } - You'll get all the keys from obj
let p2 = pick(o, ['a','c']); // { a: string , c: number }
Sign up to request clarification or add additional context in comments.

3 Comments

great answer, a couple of questions: - does type Pick represents an object with a subset of the properties of T? - (keys as string) is this how you cast in typescript?
Pick will create a new type based on K values under the condition that they exists in T, read about the utility types, they are very handy, and if you want more give a read to @basarat 's TypeScript Deep Dive
As of (keys as string), it's rather forcing a type upon a variable/function/anything. I used it in my code because the compiler expects the keys to be a string or K[], but when we reach the .split method, the typecheck knows that .split is a method that can be found on a string type and that is ok, but it will fail when it checks against the K[] because this type is not a string, therefore, using someVar as SomeType you tell the typescript compiler that "YOU_KNOW_BETTER" and that is definately the type you want to check against, and it will honor your type.
1

You can have use union

const pick = (obj: Object,  keys: string[] | string) => {
....
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.