Это моя первая универсальная функция типа c, и я буду признателен, если кто-нибудь поможет мне с отсутствующим пониманием партиалов и общего поведения TS.
Что предназначено для достижения:
1. Пройдите объект типа <T>
2. Измените его и получите ожидаемый объект типа <U>
Пример ниже с живыми предупреждениями в codeandbox: https://codesandbox.io/s/small-wood-yq14e
PS: я открыт для новых идей о том, как на самом деле следует подходить ко всему в лучших практиках TS: -)
export type ObjectWithStrings = { [index: string]: string }
// some demo data
const CONTAINER_NUTRIENTS_NAMES_ORDER: ObjectWithStrings = {}
const LIPIDS_NUTRIENTS_NAMES_ORDER: ObjectWithStrings = {}
const NUTRIENTS_MODEL_MAP: ObjectWithStrings = {}
function _sortNutrientsModel<T> (nutrientsModel: T) {
const sortedNutrientsModel: Partial<T> = {}
for (const sectionName in CONTAINER_NUTRIENTS_NAMES_ORDER) {
if (CONTAINER_NUTRIENTS_NAMES_ORDER.hasOwnProperty(sectionName)) {
/*
sortedNutrientsModel[sectionName]:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Partial<T>'.
No index signature with a parameter of type 'string' was found on type 'Partial<T>'.ts(7053)
nutrientsModel[sectionName]:
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'unknown'.
No index signature with a parameter of type 'string' was found on type 'unknown'.ts(7053) */
sortedNutrientsModel[sectionName] = nutrientsModel[sectionName]
}
}
for (const sectionName in LIPIDS_NUTRIENTS_NAMES_ORDER) {
/*
Property 'lipids' does not exist on type 'T'.ts(2339) */
if (LIPIDS_NUTRIENTS_NAMES_ORDER.hasOwnProperty(sectionName) && sortedNutrientsModel.lipids.sections[sectionName]) {
sortedNutrientsModel.lipids.sections[sectionName] = nutrientsModel.lipids.sections[sectionName]
}
}
return sortedNutrientsModel
}
export function createNutrientsModel<T extends object, U> (nutrients: T) {
const nutrientsModel: Partial<U> = {}
for (const nutrientName in nutrients) {
if (nutrients.hasOwnProperty(nutrientName)) {
const nutrientLocation = NUTRIENTS_MODEL_MAP[nutrientName]
if (typeof nutrientLocation === `string`) {
const nutrientValues = nutrients[nutrientName]
switch (nutrientLocation.length) {
case 0: /* Container nutrient section */
/*
Type 'Extract<keyof T, string>' cannot be used to index type 'Partial<U>'.ts(2536) */
if (typeof nutrientsModel[nutrientName] === `undefined`) {
/*
Type 'Extract<keyof T, string>' cannot be used to index type 'Partial<U>'.ts(2536) */
nutrientsModel[nutrientName] = {}
}
/*
Type 'Extract<keyof T, string>' cannot be used to index type 'Partial<U>'.ts(2536) */
nutrientsModel[nutrientName].values = nutrientValues
break
default: /* Nutrient section */
/*
Type 'Extract<keyof T, string>' cannot be used to index type 'Partial<U>'.ts(2536) */
if (nutrientsModel[nutrientLocation] === `undefined`) {
/*
Type 'Extract<keyof T, string>' cannot be used to index type 'Partial<U>'.ts(2536) */
nutrientsModel[nutrientLocation] = {}
}
/*
Type 'Extract<keyof T, string>' cannot be used to index type 'Partial<U>'.ts(2536) */
if (typeof nutrientsModel[nutrientLocation].sections === `undefined`) {
nutrientsModel[nutrientLocation].sections = {}
}
/*
Type 'Extract<keyof T, string>' cannot be used to index type 'Partial<U>'.ts(2536) */
nutrientsModel[nutrientLocation].sections[nutrientName] = {
values: nutrientValues
}
}
}
}
}
return _sortNutrientsModel(nutrientsModel) as U
}
ОБНОВЛЕНИЕ с примером NutrientsType
interface BalanceModelSection {
DRI: {
AI: number,
AMDR: BalanceModelAMDR
EAR: number,
RDA: number,
UL: number,
unit: string
[index: string]: string | number | BalanceModelAMDR
}
}
interface BalanceRecordSectionValues {
converted: {
quantity: number
recommended: number
unit: string
}
quantity: number
percentage: string
}
interface BalanceRecordSection extends
BalanceRecordSectionValues,
BalanceModelSection {}
interface BalanceRecordNutrients {
[index: string]: BalanceRecordSection | BalanceRecordProgressSection
energy: BalanceRecordSection
}