Reputation: 1033
I want to create a function that accepts two arguments, the first is an object that includes an array of strings that are the required fields for the second argument. e.g
const configOptions = {
config1: {
value: string,
props: ['firstProp', 'secondProp']
}
};
myFunc(configOptions.config1, {
'firstProp': 'first value', // <-- firstProp and secondProp are required because they are defined in config.props
'secondProp': 'second value',
});
I can achieve this by referencing the specific config in the function declaration and doing a const assertion on the config options
const configOptions = {
config1: {
value: 'a string value',
props: ['firstProp', 'secondProp']
}
} as const;
function myFunc<T extends typeof configOptions.config1>(
option: T,
params: { [key in typeof option.props[number]]: string }
) {}
myFunc(configOptions.config1, {
firstProp: 'first value',
secondProp: 'second value',
});
But I want to achieve this more generically so I could call the same function and use one of many configs. Is this possible?
Here is a stackblitz https://stackblitz.com/edit/typescript-ydyt4v
Upvotes: 2
Views: 818
Reputation: 5650
Using a generic with a constraint will allow inferring the type of the config
. This inferred generic can then be used to define the type for the second argument.
const configOptions = {
config1: {
value: 'string',
props: ['firstProp', 'secondProp']
},
config2: {
value: 'string',
props: ['one', 'two']
}
} as const;
// Define the base shape (most general shape) of the config.
// I used the const assertion (`as const`) above so defining the props as
// readonly made this work nicely but that could be changed.
interface BaseConfig {
value: string;
props: readonly string[]
}
const myFunc = <
// Define a generic `Config` that adheres (extends) the base shape (BaseConfig).
// It represents the first argument and is inferred from the passed `config`.
Config extends BaseConfig
>(
config: Config,
// Define the input values type based on the passed `Config`.
// Specifically, we want the values of the props key.
// And those keys are for a record, with a string value.
values: Record<Config["props"][number], string>
) => {
// TODO: implement as needed.
return null;
}
// Okay.
myFunc(configOptions.config1, {
'firstProp': 'first value',
'secondProp': 'second value',
});
// Property 'secondProp' is missing in type.
myFunc(configOptions.config1, {
'firstProp': 'first value',
});
// Okay.
myFunc(configOptions.config2, {
'one': '1',
'two': '2'
});
// Property 'one' is missing in type.
myFunc(configOptions.config2, {
'two': '2'
});
Upvotes: 3