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];
};
CodePudding user response:
In pluckOnlyStringValues
implementation, O
is unknown, so PickKeysByValue<O, string>
is unknown too. TypeScript is not clever enough to determine that obj[key]
is always of type string
You could add a function overload which is compatible with your current declaration and with the implementation.
function pluckOnlyStringValues<O extends { a: string }>(
obj: O,
key: PickKeysByValue<O, string>,
): string
function pluckOnlyStringValues(
obj: Record<string, string>,
key: string,
): string {
return obj[key];
};
CodePudding user response:
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];
};
CodePudding user response:
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