Есть ли причина, по которой отображение массива не работает так же, как отображение записи? Массив сам по себе является итерируемым объектом, как и Record<number, ...>, верно?
В этом коде вы можете наблюдать желаемое поведение ParamRecordRecord
.
Это дает мне таблицу данных с методами, у которых параметр автоматически выводится на основе контекста данных.
Еще мне нужна версия в виде записи для массивов.
Таким образом, ParamRecordArray
является точной копией ParamRecordRecord
, за исключением того, что вместо ключей, перечисленных в enum BBB
, ключи являются индексами массива.
Однако по какой-то причине я не могу воспроизвести то же поведение, что и в таблице выше.
Все параметры в компонентах методов являются числами.
пометка красного цвета🔴, где возникла проблема
enum AAA { a1 = 'a1', a2 = 'a2', a3 = 'a3' }
enum BBB { b1 = 'b1', b2 = 'b2', b3 = 'b3' }
// the data structure
type TestData<T extends number = number> = {
_: string;
components?: T[];
check: (c: T) => void;
}
// version record of record
type ParamRecordRecord<T> = {
[KA in keyof T & AAA]: {
[KB in keyof T[KA] & BBB]?: TestData<Extract<T[KA][KB], number> extends never ? number : Extract<T[KA][KB], number>>
}
}
new class TestRecorRecord<T> {
constructor(data: ParamRecordRecord<T>) { }
}(
{
[AAA.a1]: {
[BBB.b1]: {
_: '',
check: (c) => { } //🟢number
// ^?
},
},
[AAA.a2]: {
[BBB.b2]: {
_: '',
components: [1],
check: (c) => { }//🟢1
// ^?
},
[BBB.b1]: {
_: '',
components: [2],
check: (c) => { }//🟢2
// ^?
},
},
}
)
// version record of array
//This is a copy of ParamRecordRecord, except that the iteration is done over the index of the object array.
//The index keys are numbers or `${number}`, rather than strings coming from the BBB enumerator.
type ParamRecordArray<T> = {
[KA in keyof T & AAA]: {
[KB in keyof T[KA] & `${number}`]?: TestData<Extract<T[KA][KB], number> extends never ? number : Extract<T[KA][KB], number>>
}
}
new class TestRecorArray<T> {
constructor(data: ParamRecordArray<T>) { }
}(
{
[AAA.a1]: [
{
_: '',
check: (c) => { }//🟢number
// ^?
},
],
[AAA.a2]: [
{
_: '',
components: [1],
check: (c) => { }//🔴1
// ^?
},
{
_: '',
components: [2],
check: (c) => { }//🔴2
// ^?
},
],
}
)
//ok not work so let try debug ?
type debug = ParamRecordArray<{ [AAA.a1]: [ TestData<1>, TestData<2> ] }>
// ^?
// ya it seem not able to extract the generic , but am not understand why !?
Заранее благодарю за разъяснения и решение, если это возможно.
ПРИМЕЧАНИЕ. Я не хочу использовать as const
или function<const T>():T
везде для каждой записи только для версий массива.
const T
в классе может быть решение, но, похоже, оно не работает!
Я также мог бы использовать одну обертку, но тип const T
, похоже, в этом случае не обрабатывает вложенные Record<string, TestData[]>
.
Ого, да, это именно то поведение, на которое я надеялся! Это увлекательно, спасибо за решение. В конце концов я смирился с тем, что везде использую константу! Это было невыносимо!
Напишу ответ, когда будет возможность.
Спасибо. Полагаю, это связано с тем, что иногда приходится разделять типы, потому что они могут вести себя по-разному. Коллега однажды сказал мне: «Иногда отображаемые типы второго уровня ведут себя иначе, чем типы первого уровня, и лучше выделить их в новый универсальный тип».
обратите внимание, что ваша строка отладки должна выглядеть как type debug = ParamRecordArray<{ [AAA.a1]: [1, 2] }>
, а не type debug = ParamRecordArray<{ [AAA.a1]: [ TestData<1>, TestData<2> ] }>
Проблема в том, что TypeScript с массивами и типами Arrays использует числа в качестве ключей, и TypeScript не может угадать правильный тип для каждого элемента.
поэтому, чтобы исправить это, используйте вспомогательный тип, чтобы получить тип внутри массива. это поможет TS понять, какой тип следует ожидать.
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
type ParamRecordArray<T> = {
[KA in keyof T & AAA]: T[KA] extends readonly any[] ? {
[KB in keyof T[KA] & `${number}`]?: TestData<ArrayElement<T[KA]> extends number ? ArrayElement<T[KA]> : number>
} : never
}
Спасибо за ваше предложение. К сожалению, похоже, это не работает, все превращается в тип any
или может я что-то пропустил?
@jon Я проверил ссылку «игровая площадка» и ее сборку без каких-либо ошибок.
Представленная выше игровая площадка не содержит ошибок и успешно компилируется. Проблема заключается в наборе текста. Единственное нежизнеспособное решение — использовать as const
везде. Поэтому ищу альтернативу этому вопросу. Ваше предложение не вызывает ошибок, но все типы становятся any
и не закрепляют строки тегами 🔴. если вы поставите что-нибудь везде, да, приложение с компиляцией 😉
Вы столкнулись с давней ошибкой, о которой сообщалось по адресу microsoft/TypeScript#27995 , где отображаемые типы по типам массивов и кортежей не совсем ведут себя как гомоморфные отображаемые типы (см. Что означает «гомоморфный отображаемый тип» ? ), если только сопоставляемый тип не является параметром универсального типа. Если T
является параметром универсального типа, то {[K in keyof T]: F<T[K]>}
будет сопоставлять типы массивов с типами массивов, а типы кортежей с типами кортежей. Но в противном случае он будет отображать все ключи типа массива, включая такие вещи, как "length"
и "push"
. Гомоморфные отображаемые типы позволяют выводить T
из значения типа {[K in keyof T]: F<T[K]>}
, и такого рода вывод — это то, что вам нужно для работы вашего кода. Этого не происходит, поэтому вы получаете неверный вывод.
Это считается ошибкой, но неясно, когда и будет ли она когда-либо исправлена. До тех пор, пока этого не произойдет, вы можете обойти это, просто проведя рефакторинг так, чтобы отображаемый тип был параметром универсального типа; то есть создайте тип утилиты:
type ParamRecordArray<T> = {
[KA in keyof T & AAA]: F<T[KA]>
}
type F<T> = { [KB in keyof T]?: TestData<
Extract<T[KB], number> extends never ? number : Extract<T[KB], number>
> }
Здесь F<T[KA]>
делает то же, что и ваш исходный { [KB in keyof T[KA] &
${number}]?: ⋯ }
. Обратите внимание, что нет смысла пересекать с `${number}`
, поскольку все, что для этого нужно, — это нарушить гомоморфное отображение и предотвратить необходимый вывод, а гомоморфные отображаемые массивы/кортежи уже перебирают только числовые индексы.
Давайте убедимся, что это работает:
const z = new class TestRecorArray<T> {
constructor(data: ParamRecordArray<T>) { }
}(
{
[AAA.a1]: [
{
_: '',
check: (c) => { }//🟢number
// ^? (parameter) c: number
},
],
[AAA.a2]: [
{
_: '',
components: [1],
check: (c) => { }//🟢1
// ^? (parameter) c: 1
},
{
_: '',
components: [2],
check: (c) => { }//🟢2
// ^? (parameter) c: 2
},
],
}
)
Выглядит хорошо!
Детская площадка, ссылка на код
Объяснение очень ясное, и я его понимаю. Спасибо за качество вашего времени, вы потрясающие.
Я бы сказал, что ваша проблема здесь в том, что сопоставленные типы с массивами ведут себя гомоморфно только тогда, когда тип массива является непосредственно универсальным, поэтому, если вы выполните рефакторинг, как эта ссылка на игровую площадку, то он должен начать работать так, как ожидалось. Это полностью решает вопрос? Если да, то я напишу ответ с объяснением; если нет, то что мне не хватает?