Ссылка на пример:
пример
Хочется избавиться от
value: any в функции
setProp.
import * as lodash from 'lodash';
/**
* Тип позволяет получить ключи объекта вместе с вложенными.
* Использование keyof IConfig позволит получить ключи только первого уровня.
*
* @example
* interface IConfig {
* property: number
* nested: {
* nestedProp: number
* }
* }
*
* // Тут автокомплит TS предложит использовать строки "property" и "nested"
* const config: keyof IConfig = '';
*
* // А тут автокомплит TS предложит использовать строки "property", "nested" и "nested.nestedProp"
* const nestedConfig: NestedKeyOf<IConfig> = '';
*/
type NestedKeyOf<ObjectType extends object> = {
[Key in keyof ObjectType & (string | number)]: ObjectType[Key] extends object
? /**
* Тип работает как надо. Это не костыль, а вынужденная мера.
* TS считает что вложенность объекта может быть бесконечной,
* но в текущей задаче там всего 2 уровня.
*/
// @ts-ignore Просто верь мне ))
`${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}`
: `${Key}`;
}[keyof ObjectType & (string | number)];
interface IConfig {
prop: number;
nested: {
nestedProp: string;
};
}
const config: IConfig = {
prop: 1,
nested: {
nestedProp: '2',
},
};
// Здесь всё работает чётко
// NestedKeyOf справляется как надо
function getProp<T = any>(property: NestedKeyOf<IConfig>): T | null {
return lodash.get(config, property) || null;
}
// Как типизировать "value" в зависимости от "property"
// Если property равен "prop", то тип value должен быть "number", так как typeof IConfig['prop'] === 'number'
// Если property равен "nested.nestedProp", то тип value должен быть "string", так как typeof IConfig['nested']['nestedProp'] === 'string'
function setProp(property: NestedKeyOf<IConfig>, value: any): void {
lodash.set(config, property, value);
}
setProp('prop', 123);
setProp('nested.nestedProp', '123');
console.log(config);
console.log(getProp('nested.nestedProp'));
console.log(getProp('prop'));