2

I have a super simple use-case. I want have a function pluckOnlyStringValues on which I pass an object obj and a key and I want to ensure that I can pass only such keys whose values are string. In such a way that pluckOnlyStringValues always returns string.

For the goal I'm trying implementing a type helper PickKeysByValue, but it does not seem to work...

type PickKeysByValue<T extends object, ValueTypes> = {
  [K in keyof T]-?: T[K] extends ValueTypes ? K : never;
}[keyof T];

// Working
type GetKeysWithStringValues = PickKeysByValue<
  { a: string; b?: string; c: number | undefined; d: () => 4 },
  string
>;
// Working
type GetStringValues = { a: string; b?: string; c: number | undefined; d: () => 4 }[GetKeysWithStringValues]

// Not working
const pluckOnlyStringValues = <O extends { a: string }>(
  obj: O,
  key: PickKeysByValue<O, string>,
): string => {
  return obj[key];
};
2
  • 1
    why are you constraining O to { a: string }? You should replace it with Record<string, any> or Record<string, any> & { a: string } to indicate that obj can be indexed with a string. Commented Jul 5, 2022 at 14:05
  • @TobiasS. this was the issue indeed. Good catch! Would you want to answer officially in order to mark this as accepted answer? Commented Jul 5, 2022 at 15:37

2 Answers 2

1

If you change the constraint of O to Record<string, any>, TypeScript will know that O is indexable with a string.

const pluckOnlyStringValues = <O extends Record<string, any>>(
  obj: O,
  key: PickKeysByValue<O, string>,
): string => {
  return obj[key];
};
Sign up to request clarification or add additional context in comments.

Comments

0

Based on your description,..

You could do this with a helper type, StringKeys, this would then just return Keys that are string types.

const test = {
  aNumber: 12,
  aString: 'ABC'
};

type StringKeys<T extends object> = {
  [K in keyof T]: T[K] extends string ? K : never
}[keyof T];

function pluckString<T extends object>(obj:T, key:StringKeys<T>) {
  return obj[key];
}

pluckString(test, 'aString')  //ok
pluckString(test, 'aNumber'); //error

Playground with Return

7 Comments

this is not working when we try to access obj[key] within the function. function pluckString<T extends object>(obj:T, key:StringKeys<T>): string { return obj[key] }
@KamenKanev Works for me.. Updated Playground with it on.
Setting an explicit return type to the function reviels the problem. I've updated the playground: typescriptlang.org/play?#code/…
@KamenKanev Why are you setting an explicit return?, the implied type is string. If you over type Typescript your really using it wrong. Try and keep as much as you can implicit, and Typescript works with you, rather than against.
@KamenKanev This might be a useful video -> youtube.com/watch?v=RmGHnYUqQ4k&t=35s
|

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.