У меня есть функция test
в TypeScript (v5.5.4), которая принимает параметр fieldsMap
и должна возвращать определенный тип на основе ключей и значений fieldsMap
. Однако у меня возникли проблемы с правильным определением типа возвращаемого значения. Вот текущая реализация:
function test<
K extends string,
T extends { [key in K]: string[] } = { [key in K]: string[] },
>(
fieldsMap: T // { INFO: ['A', 'B', 'C'], OTHER: ['X', 'Y', 'Z'] }
): {
[A in K]: { [E in T[A][number]]: string | number }[]
} {
// TODO: Implement
console.info(fieldsMap);
return {} as any;
}
Я хочу вызвать этот метод следующим образом:
const result = test<'INFO' | 'OTHER'>({
INFO: ['A', 'B', 'C'],
OTHER: ['X', 'Y', 'Z'],
});
В настоящее время предполагаемый тип result
:
type CurrentResult = {
INFO: { [p: string]: string | number }[],
OTHER: { [p: string]: string | number }[]
};
Но я ожидаю, что тип будет:
type ExpectedResult = {
INFO: { [p in 'A' | 'B' | 'C']: string | number }[],
OTHER: { [p in 'X' | 'Y' | 'Z']: string | number }[]
};
Кроме того, следующая строка не должна компилироваться:
result.INFO.map((I) => I.WORKING); // This should not compile
Как я могу изменить свою функцию, чтобы она возвращала правильно типизированный объект?
Точно @Калет. Меня вдохновило решение @Remo H. Jansen. (1) Удалите K и выведите его с помощью keyof T
(2) Передайте fieldMap as const
. Спасибо за вашу помощь.
Вы можете попробовать следующее:
const fieldMap = {
INFO: ['A', 'B', 'C'] as const,
OTHER: ['X', 'Y', 'Z'] as const,
};
type FieldMap = typeof fieldMap;
function test<
K extends keyof FieldMap
>(
fieldsMap: FieldMap
): {
[A in K]: { [E in FieldMap[A][number]]: string | number }[]
} {
// TODO: Implement
console.info(fieldsMap);
return {} as any;
}
Для меня вывод выглядит правильным:
Я бы также предложил применить утверждение const (as const
) ко всей переменной fieldMap. Таким образом, это более очевидно и читабельно, в отличие от применения этого утверждения к отдельным клавишам. Но это скорее личный вкус, чем строгое правило.
У меня нет строгого контроля над fieldMap
, который относится к типу { [key in string]: string[] }
. Итак, я не могу использовать keyof
в «FieldMap». Однако ваше предложение использовать as const
вместо fieldMap
и не определять K
напрямую решило проблему. Пересмотренная функция: typescript function test< T extends { [key in string]: readonly string[] } >( fieldsMap: T ): { [A in keyof T]: { [E in T[A][number]]: string | number }[] }
Использование: typescript const result = client.test({ INFO: ['A', 'B', 'C'] as const, OTHER: ['X', 'Y', 'Z'] as const, });
Спасибо за решение!🙏
Если вы не укажете тип K
, это станет намного проще (хотя вам нужно указать as const
для fieldMap
, иначе будет выведено string[]
для типов fieldMap.INFO
и fieldMap.OTHER
.
Мы можем вынести манипуляции с типами в хелперы
type FieldMap<T> = T extends { [key in string]: readonly string[] } ? T : never
type TestResult<T> = T extends { [key in string]: readonly string[] } ? { [A in keyof T]: { [E in T[A][number]]: string | number }[] } : never
function test<T>(
fieldsMap: FieldMap<T>
): TestResult<T> {
// TODO: Implement
console.info(fieldsMap);
return {} as any;
}
требуется ли синтаксис
test<'INFO' | 'OTHER'>
? было бы гораздо проще, если быK
можно было вывести изkeyof T