I have the following interfaces:
interface Values {
height: number;
width: number;
}
interface Opts {
requiredKeys: (keyof Values)[];
customParsers: Record<string, (text: string) => unknown>;
}
I want to make it so Opts['requiredKeys']
can also take strings from the keys of Opts['customParsers']
; for example:
const opts: Opts = {
requiredKeys: [
'height',
'foo', // error! not a key of `Values` and also not defined in `customParsers`
'x', // allowed because `x` is defined in `customParsers`
],
customParsers: {
x: text => parseInt(text),
},
};
I've tried changing Opts['requiredKeys']
's type to (keyof Opts['customParsers'] | keyof Values)[]
but it didn't work.
I believe what I am trying to do can be achieved with generics but I am not very experienced with them and don't know how I'd implement it exactly.
The first answer has answered most of your question, except one point you mentioned:
// error! not a key of
Values
and also not defined incustomParsers
So i came up with the solution for that problem aswell: Playground link
interface Opts<T extends string> {
requiredKeys: (keyof Values | NoInfer<T>)[]; // T will not be inferred here, instead will be inferred in the Record type
customParsers: Record<T, (text: string) => unknown>;
}
function createOption<T extends string>(requiredKeys: Opts<T>["requiredKeys"], customParsers: Opts<T>["customParsers"]): Opts<T> {
return { requiredKeys, customParsers };
}
const optsNew = createOption(
[
'x',
'foo' // Type '"foo"' is not assignable to type 'keyof Values | "x"'
],
{
x: (text) => parseInt(text),
},
);